아토믹 디자인 방식으로 폴더를 구성하기

jonyChoiGenius·2023년 1월 15일
0

아토믹 디자인 방식이란?

Atom은 분리할 수 없는 단위를 의미한다.

Atomic 디자인 방식 역시 디자인의 각 요소를 분리할 수 없는 단위까지 나누어, 분리된 Atom들을 조합하여 상위의 컴포넌트를 만들고, 상위의 컴포넌트들과 데이터를 조합하여 하나의 페이지를 구성하는 방식을 의미한다.


참고 1 - 아토믹 디자인 방법론 - Brad Frost
아토믹 디자인을 활용한 디자인 시스템 도입기 - 카카오 FE 기술 블로그

아토믹 디자인 방식으로 구성된 폴더구조 예시

출처
위의 예시를 아래와 같이 시각화 하였다.
(이때, template에 데이터가 들어가있는 것으로 캡처하였지만, 실제로 template은 데이터를 받아올 뿐이다. 하기 사진에서 template라 기재된 부분은 page에 해당한다.)

위의 예제에서 사용된 방식을 참조하면,
Atomic한 컴포넌트를 만든 후,
page에서 template을 비롯한 요소들을 불러와 페이지를 구성한 후,
{props.children}으로 내려주면 된다.

Atomic 디자인 패턴은 원라는 디자인을 props에 따라 반환하도록 추상화해두면 재사용이 가능하다는 장점이 있다. 프로젝트를 소규모로 진행하는 것에 비해 추상화에 지나치게 많은 시간을 쏟을 우려가 있으나, 한 번 만든 보일러 플레이트를 여러번 재사용하게 된다면 일관된 코드 퀄리티를 유지하면서도 보다 빠르게 디자인을 할 수 있게 될 것이다.

아토믹 디자인 방식과 Next.js가 잘 맞는 이유

아토믹 디자인 방식은 하부 컴포넌트들을 'page'라는 상위 컴포넌트에서 불러와 작성하게 된다.

next.js는 pages 폴더와 그 파일명으로 라우팅을 하기 때문에 app.js에서 라우팅을 하는 기존 리액트 방식에 비해 파일과 폴더의 분리가 잘 이루어져 있고, page가 늘어남에 따라 관리가 어려워지는 문제점이나 page와 template를 어떻게 분리할 것인지 등의 문제로부터 자유롭다. 또한 pages 폴더와 components 폴더가 분리되어 있어도, 결과적으로 pages 폴더를 통해 로직을 구성하고, components 폴더 내의 아토믹 디자인들은 추상화된, 혹은 이미 정의된 props의 로직만을 처리하면 되기 때문에 개발하는 입장에서는 pages 폴더의 page로 관심사가 컴파운드 되어있다는 느낌을 받을 수 있다.

컴파운드 컴포넌트

지금까지 리액트를 배우면서 사용한 패턴은 '컴파운드 컴포넌트'라 불린다.
하나의 관심사에 대해 컴포넌트를 통합시키는 패턴이다.

컴포넌트가 통합되어 있기 때문에 개발시에 '어떻게 분리할 것인가' 등의 고민 없이 자율적으로 기능을 구현할 수 있고,
가장 상위의 컴포넌트에 다양한 함수와 프로퍼티들을 정의하고, 이를 한 뎁스, 혹은 두 뎁스 아래의 컴포넌트들에게만 전달하면 되니 Props Drilling 현상에서부터 비교적 자유로울 수 있다.

하지만 가장 큰 단점은 코드의 재사용성이 떨어진다는 점인데, 컴파운드 컴포넌트로 코드를 작성하다보면 같은 코드, 같은 디자인을 또 작성하게 되는 문제점이 발생하며, 재사용성을 높이고 Props Drilling을 해결하려다보면 지나치게 부모 컴포넌트가 비대해지고, 결과적으로 '관심사 별로 알아보기 쉽다'거나 '컴포넌트들이 자율적이다'라는 장점은 사라진체, 부모 컴포넌트를 눈이 빠지게 알아보고, 부모 컴포넌트에 자식 컴포넌트들이 종속되는 문제가 발생한다. 강력한 모듈과 객체지향 기능을 지원하는 현재의 자바스크립트에서 이와 같은 방식으로 코딩을 할 이유는 없다.

또한 '자율적'이라는 의미는 '기준이 없음'을 의미하기도 한다. 코드의 재사용성을 위해 Custom Hooks를 쓴다거나, API를 별도로 분리한다거나, 혹은 반복문을 통해 여러 자식 컴포넌트를 렌더링하거나 자식 컴포넌트별로 서로 다른 State를 가지도록 만들려다보면 결국 '컴파운드'되지 않은 상태의 컴포넌트들이 등장하게 되고, 결과적으로 패턴이 무너지기 쉽다.

(5 Advanced React Patterns - Alexis Regnaud)
(리액트 설계 가이드 Stevy
Web Developer / kakao
)

Props Getters Pattern

리액트에서 코드를 재사용하기 가장 좋은 방식은 Custom Hooks를 사용하는 것이다.
Custom Hook를 통해 자주 사용되는 함수나 State를 분리하여 보관해두면, Props Drilling 없이 자식 컴포넌트에서 곧바로 호출할 수 있다.

이러한 Custom Hooks 패턴에서, 자식 컴포넌트의 관심사별로 props를 나누어 getter를 정의한 것을 Props Getters Pattern이라 한다. 자식 컴포넌트가 부모 컴포넌트의 어떠한 State를 조작하는지를 Getters 함수를 통해 식별해두는 것이다. 복잡한 로직의 Custom Hooks를 분리할 수 있음은 불몬, 자식 컴포넌트 내에서는 비교적 일관된 식별자의 Getter들을 사용하게 된다. 다만 '복잡한 로직'을 숨기고, 이를 추상화하는 비용이 들 수 있다.

결과적으로

Atomic Design 방식으로 컴포넌트를 구성하고, 기능적인 측면에서는 utils 폴더로 분리, 모듈화하여 Custom Hooks 패턴, 더 나아가 Props Getters Pattern으로 폴더 구조를 짜 보일러 플레이트로 사용하고자 한다.

추가적으로 redux는 ducks패턴을 이용해 모듈별로 관리하고자 한다.

이를 통해 만들어진 파일 구조는 아래와 같다.

my-project
|-- pages
|   |-- index.tsx
|   |-- login
|   |   `-- index.tsx
|   `-- register
|       `-- index.tsx
|-- components
|   |-- atoms
|   |   |-- button.tsx  
|   |   |-- input.tsx   
|   |   `-- title.tsx   
|   |-- mocules
|   |   `-- block.tsx   
|   |-- organism        
|   |   `-- form.tsx    
|   `-- template        
|       `-- authForm.tsx
|-- store
|   |-- index.tsx
|   `-- modules
|       `-- auth.tsx
|-- utils
|   |-- index.tsx
|   |-- propGetters
|       `-- index.tsx
|   |-- customHooks
|   |   |-- index.tsx
|   |   |-- useDebounce.tsx
|   |   |-- useOnChange.tsx
|   |   `-- useOnSubmit.tsx
|   `-- customApi
|-- tsconfig.json
|-- jsconfig.json       
|-- package.json
`-- yarn.lock

13 directories, 21 files
profile
천재가 되어버린 박제를 아시오?

0개의 댓글