부스트캠프 프로젝트에서 Atomic Design을 응용해 프론트엔드 프로젝트 구조를 설계하고 기술 스택을 결정한 경험을 공유할까 합니다.
우측 링크에서 프로젝트 소스코드를 확인해 보실 수 있습니다. 👉 GitHub 링크
Atomic Design은 원자가 결합해 분자가 되고, 분자가 결합하여 유기체가 되는 것처럼, UI 컴포넌트를 가장 작은 단위로 쪼개고 합치며 UI를 마치 레고 블럭 쌓듯이 만들어나가는 방법입니다.
일반적인 Atomic Design에서는 아래와 같은 단계로 컴포넌트를 관리합니다.
Atoms
: 하나의 구성 요소. 본인 자체의 스타일만 가지고 있으며 다른 곳에 영향을 미치는 스타일은 적용되지 않아야 함.
Molecules
: 원자들의 모음
Organisms
: 분자들의 모음
Templates
: 유기체들을 모아 템플릿으로 생성
Pages
: 실제 페이지를 구성
저희는 기획 단계에서 컴포넌트를 재사용 가능하게 만들자!
라는 목표를 가지고 Atomic Design을 프로젝트에 적용하고자 했습니다.
왜냐하면 대부분의 프로젝트에서 Button, Input 등의 요소의 디자인은 거의 바뀌지 않고 재활용 되어서 쓰이는 경우가 많기 때문입니다. 우리 프로젝트에서도 Button, Input 등의 요소는 계속 반복되어 사용되었고, 이를 재사용 가능하게 만들고자 하였습니다.
아래 두개의 사진은 우리 프로젝트를 디자인 할 때 사용된 디자인 툴인 피그마
를 캡쳐한 사진입니다. 버튼이 계속 반복되어 사용되는 것을 확인할 수 있습니다.
첫째로, 예측했던 것처럼 컴포넌트 재사용이 매우 편리했습니다. Button
, Input
을 한번 만들어 놓으면 다시 만들 필요가 없고 가져다 사용하기만 하면 되서 개발 속도가 빨랐습니다.
또한, Atomic Design을 사용해 개발하면, 컴포넌트 단위의 분업
이 가능하여 협업하기에 무척 편리했습니다.
우리 팀은 다 같이 컴포넌트를 쪼개고, 각자 맡은 부분의 Atom을 만들었습니다. 이렇게 개발하니 서로가 독립된 환경에서 작업할 수 있어 개발 속도가 빨랐습니다.
우리는 컴포넌트 단위의 분업의 장점을 극대화 하기 위해 StoryBook과 Styled Components를 사용하기로 결정했습니다. 각각을 사용한 이유는 아래와 같습니다.
StoryBook
최소 단위의 컴포넌트를 페이지에 부착하지 않고도 제작과 동시에 바로 테스트하기 위해 사용했습니다. props의 따라서 변화하는 경우도 디자인을 비교할 수 있어 개발 속도를 향상
시키고, 동료의 작업 내용을 바로 확인할 수 있어 내가 만들지 않은 컴포넌트도 바로 확인할 수 있었습니다.
Styled Components
CSS가 컴포넌트 단위로 구성되기 때문에 CSS간 의존성이 없고, 클래스로 구분하는 작업이 불필요하고 컴포넌트의 독립성
이 보장되어 개발 속도를 높히기 위해 사용했습니다.
우리는 Atomic Design은 좋은 점도 있지만, 그에 못지 않은 단점이 있다고 판단했습니다.
우리가 판단한 단점은 아래와 같습니다.
1. Props Drilling
기존 Atomic Design에서는 재사용 가능한 컴포넌트를 만들기 위해, 아래 그림과 같이 Pages
단계에서 상태와 로직을 정의하고, 상태나 로직을 Template
, Organism
, Molecule
, Atoms
에 Props로 쭉 내려주어야 합니다.
이렇게 Props
를 내려 꽂았을 때 만약에 Page
에서 Template
으로 내려줘야 할 State의 자료형
이 바뀐다면,
Template
의 매개 변수를 바꿔줘야 하고, 이에 따라 Organism
의 매개 변수도 바꿔줘야 하고, Molecule
의 매개 변수를 바꿔줘야 하고, Atom
의 매개 변수를 바꿔줘야 합니다.
State의 자료형
이 바뀌었을 때, 상당히 많은 부분의 코드가 바뀌어야 한다는 점이 우리에게 단점으로 다가왔습니다.
2. 과연 Template를 재활용 할까?
Template
은 상태, 로직이 존재 하지 않는 하나의 Page
입니다.
우리는 Page UI
가 통채로 재활용 될 일은 없다고 판단했습니다.
이러한 상황에서 우리 프로젝트에서 Template
이 존재하는 것은 Atomic Design의 구색을 맞추기 위해 억지로 사용하는 것 같다는 생각이 들었습니다.
우리는 과도한 Props Drilling
과 Template 재활용 불가능
이라는 단점을 해결하기 위해,
어차피 재활용 되지 않을 Template
을 버리고 State
, Logic
을 Organism
단계에 심어서 Props Drilling
을 줄였습니다. 그리고 Page
는 Organism
을 배치하는 용도로 사용하였습니다.
이렇게 프로젝트 구조를 가져가게 된다면, Atom
, Molecule
을 상태에 종속되지 않게 만들어 재활용 할 수 있게 되어 Atomic Design
의 장점을 살리고, Props Drilling
을 최소화해서 Atomic Design
의 단점을 줄일 수 있었습니다.
상태들이 각각의 Organism
에 존재하니 다른 Organism에서 선언된 상태를 사용할 수 없다는 문제가 있었습니다. 우리는 이 문제를 해결하기 위해 상태 관리 라이브러리를 사용하기로 했습니다. 상태 관리 라이브러리를 사용하면 상태를 여러곳에서 사용할 수 있어 우리의 문제를 해결할 수 있었습니다.
상태 관리 라이브러리를 사용하기로 마음먹은 뒤에는 어떤 라이브러리를 사용할지에 대한 고민이 시작되었습니다.
우리는 Redux, Context API, Recoil 총 3개의 라이브러리를 후보에 올리고, 각각의 라이브러리에 대해 학습한 뒤 회의를 거쳐 라이브러리를 선정하였습니다.
우리는 결론적으로 아래와 같은 이유로 Redux를 쓰기로 결정했습니다.
Context API는 React 전역 상태 관리를 위해 제공하는 기본적인 방법이지만, 기능별로 Context를 만들어야 한다는 수고로움과 Context의 상태가 바뀔때, 해당 Context의 Provider 내부 컴포넌트가 전부 리렌더링 되는 문제, 비동기 처리를 관리하기 까다롭다는 단점이 있어 사용하지 않기로 하였습니다.
Recoil은 Facebook이 만든 상태 관리 라이브러리 입니다. 단순하고 사용하기 편리했지만, 2020년 12월 12일 기준 0.1.2 버전으로, 아직 안정화가 되지 않은 상태이고, 레퍼런스가 부족하기 때문에 사용하지 않기로 하였습니다.
Redux는 비동기 처리를 위한 미들웨어(Redux-thunk)가 편리하고, 글로벌 상태를 하나의 객체안에 넣어서 사용하기에 Context API를 사용할 때 처럼 여러 Context를 선언할 필요가 없어서 사용하기 편리하기에 사용을 결정하였습니다.
내용을 종합하여 최종 프로젝트 구조를 아래와 같이 결정했습니다.
┌── node_modules
├── src
│ ├── @types
│ ├── assets
│ ├── components
│ │ └── atoms
│ │ └── molecules
│ │ └── organisms
│ ├── stories
│ ├── modules
│ │ ├── index.ts - 리듀서 합치는 곳
│ │ ├── 각각의 module - 액션, 액션 생성 함수, 리듀서
│ ├── hooks - Custom Hook, useSelector, useDispatch로 리덕스 상태 Get
│ ├── pages
└───└── utils
├── app.tsx
├── index.tsx
├── index.html
├── package.json
├── tsconfig.json
├── eslint.json
└── webpack.config.ts
프로젝트 기술 스택은 아래와 같이 결정되었습니다.
Spec | ||||||
---|---|---|---|---|---|---|
Description | TypeScript | React | Redux | Styled-Components | Storybook | SocketIO |
Atomic Design 패턴을 그냥 맹목적으로 도입하지 않고 장단점을 판단해 일부는 적용하고, 일부는 적용하지 않고 사용해 보았습니다.
앞으로 기술을 사용할 때, 이 기술을 왜 쓰는지, 기술의 장단점이 무엇인지 파악하고 사용하는 개발자가 되도록 노력해야 겠습니다.
잘 보고 가요~