티스토리 뷰

Project Structure

storybook and atomic design

심재철 2020. 3. 23. 16:27

아토믹 디자인은 컴포넌트를 5개의 단계로 나눈다.

Atoms(원자)

button, icon, input등과 같이 컴포넌트의 최소단위를 의미한다. (보통 HTML 태그가 원자의 단위가 된다.) 원자는 컬러, 폰트, 애니메이션등을 포함한다.

MoleCules(분자)

원자를 결합하여 분자를 만든다. INPUT 원자와 BUTTON 원자를 합쳐 검색창 분자를 만들수있다.

MoleCules

Organisms(유기체)

분자들을 결합하면 유기체가 된다.

검색창 분자와 메뉴 분자 두개가 합쳐서 헤더라는 유기체를 만들었다. 유저는 유기체 단위로 사이트를 인식하기 시작한다. 유기체는 독립적이어야 하며(standalone), 쉽게 여기저기 옮길수 있어야하고(portable), 재사용(reusable)이 가능해야한다.

 

Template(템플릿)

유기체를 조합하면 드디어 사이트가 완성되기 시작한다.

템플릿은 보통 와이어프레임으로 먼저 제작된 후에 천천히 컨텐츠로 대체된다. 디자인에서의 템플릿은 와이어프레임으로 볼 수 있고, 리액트에서의 템플릿은 레이아웃 컴포넌트라고 볼 수 있다. (children을 props로 받아 감싸주는 컴포넌트)

 

Pages

페이지는 템플릿으로 찍어낸 하나의 장면이다. 템플릿에서 여러장의 페이지를 찍어낼수있는것이다. 실제로 유저가 보는 완성된 화면이다. 

아토믹 디자인에서는 이렇게 작은 단위의 컴포넌트를 모아서 하나의 페이지를 완성해 나간다.

 

왜 아토믹 디자인을 사용해야하는가?

디자인 시스템을 한마디로 정의하면, "디자인 요소들을 나누어 파악하고 이 요소를 조합하여 최종적인 화면을 완성해 나가는 방식"이라고 할 수 있다. 요즘 프론트엔드 개발 프레임워크(리액트, 앵귤러, 뷰)등도 컴포넌트 단위로 화면을 개발한다. 이러한 흐름에 비추어봤을때, 독립적인 단위의 디자인 요소들을 만들고 조합하는 방식을 사용하여 두 분야(디자인, 프론트엔드)간의 간극을 줄이는것이 협업에 큰 효율을 가져다 준다.

 

또한 전체 시스템에서 어떠한 UI 요소들이 사용되고 있는지를 스토리북으로 문서화하여 관리하기도 편하다.

스토리북 + 아토믹 디자인 보일러플레이트

https://github.com/danilowoz/react-atomic-design

 

위 보일러플레이트엔 이런말이 나온다. 아토믹 디자인이 적용된 프로젝트는 다음 규칙을 지켜야 한다.

 

1. 원자에 margin이나 position과 같은 위치에 영향을 주는 속성을 지정하지 않는다. (페이지내에서 사이드 이펙트를 일으킬 수 있음)

2. 컴포넌트들은 상태를 가지지 않아야 한다.

3. 오직 분자나, 유기체만이 원자들의 위치를 지정할 수 있으며 마찬가지로 자기 자신의 position과 margin을 지정해선 안된다.

4. 템플릿은 오직 그리드만을 가져야하고, 특정 컴포넌트의 위치를 지정해선 안된다.

5. 템플릿위에 컴포넌트를 렌더링하여 페이지를 완성시킨다.

6. 변수를 모아두는 파일(테마나 색상 변수값들)을 만들고 모든 아토믹 컴포넌트가 이 변수를 사용하게끔 만든다.

 

새로운 컴포넌트를 만들때 우리는 이러한 질문을 스스로에게 해봐야 한다.

 

"이 컴포넌트를 다른곳에 가져다 써도 문제가 없을만큼 독립적이고 특정성이 없게 잘 만들었는가?"

 

보일러 플레이트에서 구성해둔 프로젝트 폴더구조이다.

우선, 가장 큰 그룹으로 Variables, Atoms, MoleCules, Organisms, Templates, Pages로 구성되어 있다.

원자에는 버튼, 카드, 타이틀과 같은 원자적 단위의 컴포넌트들이 들어가있다. 나머지는 참고만 하라고 비워둔것같다.

 

참고 레포지토리

https://github.com/diegohaz/arc/tree/master/src-example/components

 

구조가 정말 잘 정리가 되어 있고 심지어 위키도 잘 정리 되어 있어서 참고용으로 가장 좋은 레포지토리가 아닌가 싶다.

 

원자에 해당하는 컴포넌트 폴더 안에 

이런식으로 컴포넌트파일, 스토리파일, 테스트파일이 모여있음.

 

TEMPLATE

템플릿이 뭔지 잘 감이 안왔는데 이 예시 레포지토리 덕분에 이해할수 있게 되었다. 

 

위키 살펴보기 (링크)

원자는 보통 HTML 엘리먼트로 하나로 구성된다.

const Input = props => <input {...props} />
const Label = props => <label {...props} />

분자는 원자의 조합이다. 난 처음에 조합이라고해서 항상 같은 레벨의 원자 두개를 합쳐야 분자가 되는건 줄 알았는데 이런식으로 원자 안에 원자를 내포 시키는것도 조합이라고 치는 것 같다.

const Field = ({ label, ...inputProps }) => (
  <Label>
    {label}
    <Input {...inputProps} />
  <Label>
)

유기체는 분자의 조합이다. 위 필드 2개를 합치면 유기체가 된다.

const Form = (props) => (
  <form {...props}>
    <Field label="Name" type="text" />
    <Field label="Email" type="email" />
  <form>
)

템플릿은 쉽게 말해 레이아웃 역할을 하는 컴포넌트라고 생각하면 된다.

const PageTemplate = ({ header, children }) => (
  <main>
    {header && <div>{header}</div>}
    {children}
  </main>
)

유기체끼리 조합하면 드디어 페이지가 완성된다. (폼과 헤더를 합쳐 페이지 구성, HomePage를 PageTemplate으로 찍어냈다는것에 주목)

const HomePage = () => (
  <PageTemplate header={<Header />}>
    <Form />
  </PageTemplate>
)

 

 

중요!!!

아토믹 디자인은 하나의 솔루션이어야지 또다른 문제를 만드려고 쓰는게 아니다. 내가 컴포넌트를 만들려고하는데 도대체 이게 원자인지 분자인지 유기체인지 애매한 경우가 많다. 그럴때 고민하지말고 그냥 아무데나 둬라. 너무 많은 생각을 하지마라.

그냥 둔 다음에 나중에 얼마든지 폴더를 드래그앤드랍으로 변경해주면 된다. 이게 가능한 이유는 components/index.js에서 dynamic export 해주고 있기 떄문이다. 덕분에 우리는 이렇게 컴포넌트를 쉽게 불러올 수 있다.

import { Input, Label, Field, HomePage, PageTemplate } from 'components'

이게 어떻게 가능한건지 components/index.js를 살펴보자.

 

const req = require.context('.', true, /\.\/[^/]+\/[^/]+\/index\.js$/)

req.keys().forEach((key) => {
  const componentName = key.replace(/^.+\/([^/]+)\/index\.js/, '$1')
  module.exports[componentName] = req(key).default
})

차근히 살펴보자.

위에서 사용된 정규표현식은 이러한 파일의 경로를 나타내기 위해 사용되었다.

아래 구문을 해석하려면 먼저 require.context가 뭔지 알아야 한다.  (링크)

 

components/index.js에서 

const req = require.context('.', true, /\.\/[^/]+\/[^/]+\/index\.js$/)

를 해주고 있는데, 여기서 정규표현식은

 

atoms/Atom/index.js

molecules/Molecule/index.js

organisms/Organisms/index.js

templates/Template/index.js

pages/Page/index.js

 

이렇게 각 폴더 안쪽에 있는 index.js를 불러오기 위해 사용하는것이다. 첫번째 인자 '.', 두번째 인자 true는 현재 디렉토리의 하위 디렉토리를 모두 탐색해서 정규표현식에 해당하는 모든 모듈을 찾겠다는 의미이다. 그다음 이부분이 실행된다.

 

req.keys().forEach((key) => {
  const componentName = key.replace(/^.+\/([^/]+)\/index\.js/, '$1')
  module.exports[componentName] = req(key).default
})

 

 

첫번째줄에서 컴포넌트의 이름을 추출하고 있다. 잘 모르겠을땐 직접 해보는게 최고다.

이런식으로 정규표현식에서 캡쳐된 첫번째 텍스트를 뽑아내면 컴포넌트의 이름을 뽑아 낼 수 있다.

그다음, module.export["컴포넌트이름"]에 = req("각 파일의 경로").default를 해주고 있다.

 

components폴더 내에 있는 모든 index.js에서 default로 export된 모듈들을 가져와서 module.export에 컴포넌트 이름으로 등록을 해주고 있는건데, 이렇게 하면 웹팩이 디펜던시 그래프를 그릴 수 있게 된다. 그래서 다음과 같이 매우 편리하게 import 해올 수 있게 된다.

 

import { Input, Label, Field, HomePage, PageTemplate } from 'components'

 

 

 

 

 

 

 

 

출처

https://medium.com/inturn-eng/atomic-design-and-our-component-library-42ae1988aa4f

https://brunch.co.kr/@ultra0034/63

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함