본문 바로가기
Development/Front-end

리액트 atomic design패턴과 스토리북 활용하기(Typescript)

by 휴몬이 2020. 1. 16.

안녕하세요 휴몬 랩의 개발팀의 진(JIN)입니다.

오늘은 리액트를 공부하며 많은 디렉터리 구조와 패턴들이 있는데 그중 atomic design과 DDD(Design Driven Development)라는 디자인 주도 개발환경으로 atomic design을 더 분명하게 해 줄 'storybook'에 대해 알아보겠습니다.

Atomic Design

 회사에서 사용하는 기존 웹 프로젝트가 atomic design 디렉터리 구조로 되어 있었습니다. 보통 관련도가 높은 파일들로 묶어 페이지별로 디렉터리 구조를 구성하는 편이었는데, atomic design구조는 기본 페이지별로 나누는 것은 같지만 원자(atom) - 분자(molecule) - 유기체(orginism) - 템플릿(template) - 페이지(page)의 순서로 컴포넌트를 붙여가며 단위를 키워 가는 디자인 패턴입니다.

 컴포넌트 기반인 리액트를 이용한다면 굉장히 효율적인 디렉터리 구조z로 만들 수 있을 것 같아서 리뉴얼을 하거나 프로젝트를 처음부터 세팅하게 된다면 atomic design으로 디렉터리를 구성하는 것도 재밌을 것 같습니다.

물론, 디렉터리 구조는 답이 있는 건 아니니 취향껏 팀원들과 협의하고 선택하시면 될 것 같습니다.

 

 

atomic design 구성도 [출처 : https://bradfrost.com/blog/post/atomic-web-design/]

Atomic Design 구분 기준

컴포넌트가 logic을 가지나? 

yes => orginism  예시)  Header / Form / NavigationBar ...

no = >  컴포넌트 안에 다른 컴포넌트가 있나?

           yes => molucule 예시)  TextField(Material UI) / SearchBox(Input + Button)...

           no  => atom  예시)  html의 태그 객체. Input / Button / CheckBox / Division...

 

정리

atom

가장 작은 단위로 추상적이고 그 자체로는 무엇을 못하는 단위 (html tag단위)

 

molecule

하나 이상의 컴포넌트로 구성돼 있고, 하나의 일을 하고 잘하는 단위

 

organism

여러 개의 원자들과 분자들의 조합으로, 분자들을 구성하여 독립적이고 재사용한 구성을 만든 단위

 

template

여러 개의 다른 유기체 그룹으로 구성되며 디자인 HTML전체적인 레이아웃이 보이는 그룹

 

page

템플릿에서 실제 콘텐츠가 바인딩되어 유저에게 보이고 사용되고 리뷰될 단계 

 

 처음에 atomic design으로 원자 단위로 쪼개 놓고 디렉터리 구조도 어색하고 원자, 분자, 유기체...라는 단어를 사용해서 화학 시간인 것 같은 느낌이었습니다. 실제 atomic design을 사용해보니 컴포넌트 단위로 쪼개는 리액트의 철학을 잘 살린 구조라는 인상을 많이 받았습니다. 

 컴포넌트들을 많이 만드는 수고를 초기에 해 놓으면 수정하고 UI 개발하는 건 정말 쉽게 나옵니다! 하지만, 컴포넌트 state관리가 더 힘들어질 수 있을 것 같다는데 추후에 Redux와 hook을 이용해서 관리하고 리뷰해보겠습니다:)

 


Storybook

atomic design을 이용하다 storybook을 이용해 보기로 했습니다. 

Storybook은 UI 컴포넌트를 독립적으로 그리고 효과적으로 볼 수 있는 오픈 소스 툴입니다.

디자인 주도 개발(DDD: Design Driven Development)라고 디자인이라고 생소한데 '기획 - 디자인 - 개발'을 하면서 기타 이해 관계자들과 시각적으로 원활하게 커뮤니케이션할 수 있다는 장점이 보였습니다. 게다가 atomic design으로 단위별로 시각화해서 확인하고 수정할 수 있다는 점에서 활용도가 높았습니다.

 

설치 (Typescript 적용)

 

처음 프로젝트를 만들면서 스토리북 세팅하기

npx -p @storybook/cli sb init --type react

기존 프로젝트에 스토리북 설정하기

    *주의 : package.json과 같은 경로에서 위의 명령으로 설치해줘야 합니다.
npx -p @storybook/cli sb init --type react_scripts

  - typescript와 관련된 패키지를 미리 설치해줍니다.

npm install -D react-docgen-typescript-loader 
npm install -D typescript

react-docgen-typescript-loader : 컴포넌트 props에서 사용된 타입들을 추출해 도큐먼트로 만들어주는 도구입니다. 타입들을 명확하게 확인할 수 있습니다.

 

. storybook이 생긴 것을 확인했다면 성공했습니다. 

. storybook에 config.ts가 생겼습니다. 여기서 '../stories' 부분은 어디서 스토리를 불러올지 알려주는 경로인데 src안에서 원자 , 분자, 유기체 등등 모두 생성해서 한 번에 볼 예정이므로 '../src'로 수정해합니다. 그리고 tsx파일로 작성했기 때문에 저는 tsx로 변경했습니다.

 

config.ts

import { configure } from '@storybook/react';

// automatically import all files ending in *.stories.js
configure(require.context('../stories', true, /\.stories\.js$/), module);

수정 후

import { configure } from '@storybook/react';

// automatically import all files ending in *.stories.js
configure(require.context('../src', true, /\.stories\.tsx?$/), module);

 

awesome-typescript-loader로 설정하기도 있지만  document에서 간단하게 매뉴얼대로 하실 수 있고 이 글에서는 babel-loader로 설정해보겠습니다.

 

 

 

babel-loader 설정하기

webpack설정을 커스텀해줘야 합니다.. storybook경로에서 webpack.config.js를 생성하여 storybook의 webpack을 커스텀 설정해줍니다.

module.exports = ({ config, mode }) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    use: [
      {
        loader: require.resolve('babel-loader'),
        options: {
          presets: [['react-app', { flow: false, typescript: true }]],
        },
      },
      require.resolve('react-docgen-typescript-loader'),
    ],
  });
  config.resolve.extensions.push('.ts', '.tsx');
  return config;
};

ts, tsx확장자를 babel-loader / react-docgen-typescript-loader를 사용하도록 설정했습니다. 

 

프로젝트에서 컴포넌트 스토리 생성하기

 

해당 프로젝트 구조

src
|--components
   |--atoms
     |--index.stories.tsx
     |--Button.tsx     
     |--Typography.tsx
     |--FlexBox.tsx
      ...
   |--molecules
     |--index.stories.tsx
     |--SearchBox.tsx
     |--TextField.tsx
      ...
   |--organisms
     |--index.stories.tsx
     |--LoginForm.tsx
      ...

atoms/index.stories.tsx

import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { colors, getColor } from 'commons/styles';

import Button from './Button';
import Typography from './Typography';
import FlexBox from './FlexBox';
import Image from './Image';
import AbsoluteBox from './AbsoluteBox';
import Icon from './Icon';


storiesOf('Atoms', module)
  .add('AtomButton', () => (
    <Button border="2px" backgroundColor="red">
      btn
    </Button>
  ))
  .add('Typograph', () => (
    <Typography fontSize="30" color="#787878">
      text
    </Typography>
  ))
  .add('Flexbox', () => <FlexBox flex="1">flexbox</FlexBox>)
  .add('LOGO IMAGE', () => (
    <Image
      width={148}
      height={21}
      src={require('assets/images/Navigation/logo.png')}
      alt="logo"
    />
  ))
  .add('AbsoluteBox', () => (
    <AbsoluteBox
      top={0}
      left={0}
      right={0}
      bottom={0}
      backgroundColor={'deepBlue'}
      width={400}
      height={400}
    >
      Absolutebox
    </AbsoluteBox>
  ))
  .add('visibility_off_Icon', () => (
    <>
      <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
      <Icon isButton color={'paleGrey'} >{'visibility_off'}</Icon>
    </>
  ))
  .add('visibility_Icon', () => (
    <>
      <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
      <Icon color={'deepBlue'}>{'visibility'}</Icon>
    </>
  ));

해당 프로젝트에서는 위와 같이 각 atomic design패턴에서 각 폴더에 index.stories.tsx로 만들어 주었습니다. 컴포넌트가 추가되면 add 하는 방식으로 사용하고 있습니다. 각각의 폴더를 만들어 그 안에서 stories.tsx를 만드는 경우도 많이 있는 것 같습니다! 정답이 있는 건 아니니 생각해보시고 더 적합한 구조로 만들어가시면 좋겠습니다.

 

 

[references] 

- https://storybook.js.org/docs/basics/introduction/

 

https://storybook.js.org/docs/basics/introduction/

Introduction Edit this page Storybook is a user interface development environment and playground for UI components. The tool enables developers to create components independently and showcase components interactively in an isolated development environment.

storybook.js.org

- https://velog.io/@velopert/storybook-typescript-props-documentation

 

Storybook 프로젝트에서 TypeScript로 컴포넌트의 props 문서화 편하게 하기

TypeScript 를 사용하면 JavaScript의 불편함을 해결해줄 수 있고, IDE를 더욱 적극적으로 활용 할 수 있게 해줍니다. 특히, 리액트 컴포넌트를 TypeScript 로 작성하면, PropTypes 를 완전히 대체 할 수 있고 훨씬 유용하고 편합니다. 아직 TypeScript 를 사용해본적이 없다면, 타입스크립트 기초 연습 블로그 포스트를 ...

velog.io

- https://bradfrost.com/blog/post/atomic-web-design/

댓글