IT/WEB

Atomic Design을 그만두고 디렉토리 구조를 바꾼 이야기

개발자 두더지 2023. 7. 30. 19:45
728x90

일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.

 

  본격적으로 들어가기에 앞서 Atomic Design에 대해서는 이전에 포스팅을 한 적이 있기 때문에 상세한 내용을 알고 싶은 사람은 이 포스트를 참고해주길 바란다.

 

 

왜 Atomic Design을 그만뒀는가?


 Atomic Deisgn 원리에서는 인터페이스를 기능이나 보여지는 부분의 정밀도를 바탕으로 다섯 개의 레벨으로 분류하는 것을 제안하고 있지만, 데이터의 획득을 어느 레벨에서 실행하는가등 로직을 어떻게 다룰까에 대해서 별다른 언급을 하고 있지 않다.

 또한, 독자 룰을 규정하는 것으로 Atoms/Molecules/Organisms/Template/Pages의 분류를 어떻게 할지에 대한 고민을 하지 않게 할 수는 있겠지만, 이정도까지 상세하게 분류할 필요가 있는가에 대해서 생각하게 된 것이 가장 큰 이유다. 

 "Atoms/Molecules등의 범용적, 추상적인 컨포넌트"와 "특정 문맥, 컨텐츠에서만 사용하지 않는 컴포넌트인 Organisms"를 나누는 것으로 얻어지는 메리트는 크긴하지만 "Atoms"와 "Molecules"를 나누는 것으로 얻어지는 메리트는 엄청 크지 않다. 더욱이 Pages이라는 층도 우리 서비스에서는 그닥 쓰지 않게 됐다. 

 

 

새로운 프로젝트 생성을 계기로 디렉토리 구성을 재구성


Atomic Design 사용시의 디렉토리 구조

 Atomic Design를 채용했을 때의 프로젝트 디렉토리 구조는 다음과 같았다.

.
├── assets
├── src
│   ├── main
│   │   ├── entrypoints                          # 1
│   │   │   └── [Rails Subsystem Name]
│   │   │       └── [Rails Controller Name]
│   │   │           └── [Rails Action Name].tsx
│   │   ├── components
│   │   │   ├── mountUnits                       # 2
│   │   │   │   └── [PageName].tsx
│   │   │   ├── pages                            # 3
│   │   │   │   └── [Component Name]/
│   │   │   ├── templates                        # 4
│   │   │   │   └── [Component Name]/
│   │   │   ├── organisms                        # 5
│   │   │   │   └── [Component Name]
│   │   │   ├── molecules                        # 6
│   │   │   │   └── [Component Name]
│   │   │   │       ├── index.tsx
│   │   │   │       ├── presenter.tsx
│   │   │   │       └── style.tsx
│   │   │   ├── atoms                            # 7
│   │   │   │   └── [Component Name]/
│   │   │   └── provider
│   │   ├── ducks
│   │   ├── hooks
│   │   ├── styles
│   │   └── utils
│   └── stories
│       ├── components
│       │   ├── atoms
│       │   │   └── [Component Name]
│       │   │       └── index.stories.tsx
│       │   ├── molecules
│       │   ├── organisms
│       │   └── templates
│       └── utils
├── tests
│   ├── main
│   │   ├── components
│   │   │   ├── atoms
│   │   │   │   └── [Component Name]
│   │   │   │       └── index.test.tsx
│   │   │   ├── molecules
│   │   │   ├── organisms
│   │   │   ├── pages
│   │   │   ├── provider
│   │   │   └── templates
│   │   ├── ducks
│   │   ├── hooks
│   │   └── utils
│   ├── stories
│   └── utils
└── types

 src/main/components아래에는 Atomic Design의 각층마다 디렉토리를 깊이 만들어 내서 그 안에 컴포넌트 단위의 디렉토리를 만들었다.

 그외에도 프로젝트안에는 마운트할 첫 번째 루트의 컴포넌트를 저장할 mountUnits 디렉토리나 Rails의 View에 대응한 파일을 정리한 entrypoints 디렉토리가 존재한다. 

 각 디렉토리에 대한 설명은 다음과 같다.

#1:entrypoints

 페이지마다 만든 파일이다. Ruby on Rails의 Controller-Action과 1:1대응되도록 만들었다. ReactDOM.render()를 호출해 React요소를 호출하는 것은 이 레이어이다. 마운트할 곳인 Rails의 세계와의 다리 역할을 한다.

#2:mountUnits

 entrypoints의 ReactDOM.render() 첫 번째 인수에 지정할 컴포넌트는 이 부분이 된다. React를 페이지상에 부분 도입하고 있으므로 ReactDOM.createPortal()을 이 레이어에서 호출하고 있다.

#3:pages

 ReactDOM.createPortal()의 첫 번째 인수에 지정할 컴포넌트이다. Atomic Design의 pages층에 해당하지만, 단순히 templates를 호출할 뿐인 wrapper이다.

#4:templates

 Atomic Design의 templates층에 해당한다. 부분 도입한 범위 내의 레이아웃을 정하며 별도의 특별한 로직은 없다.

#5:organisms

 Atomic Design의 organisms층에 해당한다. 서비스로서 의미가 있는 단위이가 되며, Context로의 접속이나 비동기통신도 이 층에서라면 가능하다.

#6:molecules

 Atomic Design의 molecules층에 해당한다. 범용적으로 사용할 수 있는 컴포넌트이지만, 다른 atoms 혹은 molecules층에 의존하고 있다.

#7:atoms

 Atomic Design의 atoms층에 해당한다. 범용적으로 사용할 수 있는 컴포넌트로 다른 컴포넌트에 의존하지 않는다.

각 컴포넌트 디렉토리내의 파일 구성

 Atomic Design층 각 디렉토리내에는 컴포넌트마다 디렉토리가 배치되어 그 안에 파일은 다음과 같은 구성으로 되어 있다.

  • index.tsx : Container층(JS 로직)
  • presenter.tsx : Presenter층 (DOM 구조의 관리)
  • style.ts : Style층(보여지는 부분 조정)

 또한 개발할 때에만 사용하는 Stroybook용 파일을 <RootDir>/stories/아래에, Unit test용 파일을 <RootDir>/tests/아래에 두었고, 디플로이시에 필요한 프로덕트 코드와 완전히 나눠서 관리한 것이 특징이다.

 

새로운 디렉토리 구조

 한편으로, 새롭게 만드는 프로젝트에서는 Atomic Design을 그만둔것만이 아니라 다른 구조도 대폭으로 수정하고 아래와 같은 디렉토리 구성으로 했다.

├── app
│   ├── entrypoints                              # 1
│   │   └── [Rails Subsystem Name]
|   │       └── [Rails Controller Name]
│   │           └── [Rails Action Name].tsx
│   ├── components
│   │   ├── pages
│   │   │   └── [PageName]
│   │   │           ├── index.tsx                # 2
│   │   │           ├── index.test.tsx
│   │   │           ├── hooks.test.tsx
│   │   │           ├── hooks.ts
│   │   │           ├── presenter.test.tsx
│   │   │           ├── presenter.tsx
│   │   │           ├── portals/                 # 3
│   │   │           └── [Component Name]/        # 4
│   │   ├── projects                             # 5
│   │   └── uiParts                              # 6
│   │       ├── [Component Name]
│   │       │   ├── index.stories.tsx
│   │       │   ├── index.tsx
│   │       │   ├── presenter.test-d.tsx
│   │       │   ├── presenter.test.tsx
│   │       │   ├── presenter.tsx
│   │       │   └── style.tsx
│   ├── contexts
│   ├── ducks
│   ├── hooks
│   ├── styles
│   ├── types
│   └── utils

변경점을 간단히 정리하자면 다음과 같다.

  • 범용적인 컴포넌트인 이전의 atoms와 molecules를 uiParts에 정리했다.
  • 단순히 wrapper역할이었던 이전으 pages를 templates로 정리했다.
  • 이전 organisms를 "특정 페이지에서만 사용하는 것"과 "페이지에 전반적으로 사용하는 것"으로 나눴다.
  • 특정 페이지에서만 사용하는 컴포넌트는 해당 페이지의 디렉토리 안에 배치했다.
  • 분산됐던 Unit test용의 파일이나 Stroy book용의 파일을 하나의 컴포넌트 디렉토리로 정리했다.

 그럼 각각의 상세 내용에 대해서 이야기하도록 하겠다.

#1:entrypoints

 이전의 entrypoints와 동일하게 페이지마다 생성하는 컴포넌트이다. Ruby on Rails의 Controller-Action과 1대1이 되도록 만들고, 마운트할 곳인 Rails 세계와의 다리 역할을 담당한다.

 ReactDOM.render()를 호출해 React요소를 호출하는 것은 이 레이어이다.

#2: pages/[Page Name]/index.tsx

 이전의 mountUnits이다. entrypoints의 ReactDOM.render()의 첫 번째 인수로 지정하는 컴포넌트가 이 부분이다. 컴포넌트마다 React화하고 있으므로, ReactDOM.createPotral()을 이 레이어에서 호출하고 있다.

#3:pages/[Page Name]/portals

 구 pages겸 templates이다. 상위층의 ReactDOM.createPortal()의 첫 번째 인수로 지정하는 컴포넌트가 이것이다. 각 portal은 특정 page에서만 호출되지 않으므로, pages/[Page Name]/디렉토리내에 배치하는 구조로 되어 있다.

#4:pages/[Page Name]/[Component Name]/

 이전의 organisms이다. 특정 페이지에서 호출되지 않은 것은 components/pages/[Page Name]/내에 컴포넌트명으로 디렉토리를 나눠서 배치한다.

#5:projects

 이전의 organisms이다. 페이지 전반적으로 걸쳐서 호출되는 공통의 기능을 projects이라는 디렉토리에 정리했다. 각각 페이지에 사용되지만, 범용적이지는 않고 특정의 용도로만 이용되지 않은 기능이다.

#6: uiParts

 이전의 atoms와 molecules를 정리하여 uiParts이라는 디렉토리에 저장했다. 이 레이어에 있는 것은 디자인 시스템의 UI라이브러리에 올려야하는 범용적인 컴포넌트이므로 이 중 컴포넌트만 Stroybook에 기재하기로 했다.

각 컴포넌트 디렉토리내의 파일 구성

 각 컴포넌트 디렉토리의 파일 구성은 아래와 같다.

  • index.tsx : Container층 (CustomHooks와 Presenter을 호출한다) 
  • index.test.tsx : Container층의 test
  • presnter.tsx : Presenter층 (DOM 구조의 관리)
  • presenter.test.tsx : Presenter층의 test
  • hooks.ts : 커스텀 훅
  • hooks.test.ts : 커스텀 훅의 test
  • style.tsx : Style층 (보여지는 부분의 조정)
  • index.stories.tsx : Storybook (uiParts에만 존재)

 Unit test 파일, Stroybook 파일을 동일 디렉토리에 정리했을 뿐만 아니라 hooks.ts를 만들어 로직을 커스컴 훅으로 최대한 나눴다.

 하나의 디렉토리로 정리한 이유는 개발시의 파일 작성을 효율화하기 위해 그리고 생성을 잊어 버리지 않기 위해이다. 

 그대신에 utils/폴더내가 엉망이 되는 것이 방지하기 위해 package에 대응하는 디렉토리를 나눠서 거기에 관련된 utility나 설정 파일을 저장해도록 했다.

├── app
│
// 생략
│
├── .jest
│   ├── fileTransformer.js
│   └── setupTest.ts
├── .msw
│   ├── handler.ts
│   ├── importOpenAPISpec.ts
│   ├── loadOpenAPISpec.ts
│   ├── openAPISpec.ts
│   ├── openAPIUtils.ts
│   ├── server.ts
│   └── worker.ts
├── .storybook
│   ├── main.ts
│   ├── preview-head.html
│   └── preview.tsx
└── .webpack
    ├── HookToManifestPlugin.ts
    └── PatchHtmlWebpackPluginPlugin.ts

참고자료

https://note.com/tabelog_frontend/n/n07b4077f5cf3

728x90

'IT > WEB' 카테고리의 다른 글

[Vue3] Vue3의 Composition API  (0) 2023.09.03
[CSS] Scoped CSS에 있어서의 CSS 설계 방법  (0) 2023.08.21
[Spring] @Autowired  (0) 2023.07.09
클린 아키텍처2  (0) 2023.02.15
클린 아키텍처1  (0) 2023.02.13