6. CRA로 만든 프로젝트에 storybook 적용

우리가 디자인 시스템을 만들때만 스토리북을 사용 할 수 있는건 아닙니다. 꼭 디자인 시스템이 아니여도, 기존에 존재하던 웹 프런트엔드 프로젝트에 스토리북을 적용하면 컴포넌트 문서화를 할 때 편하므로 매우 유용합니다.

디자인 시스템을 만들기 위하여 프로젝트를 아예 분리하는것이 번거로운 상황이라면, 기존 프로젝트에 바로 적용하는것도 나쁘지 않은 선택입니다. 추가적으로, 디자인 시스템에 해당하지 않는 컴포넌트들도 문서화를 제대로 하고자 한다면 Storybook을 적용하면 좋겠지요?

방법은 매우 간단합니다. CRA 로 만든 프로젝트 디렉터리에서 다음 명령어를 실행하시면 됩니다. (새로운 프로젝트를 만드시거나, 5-2 에서 만들었던 sampleapp 프로젝트에서 바로 작업을 진행하시면 됩니다.)

npx -p @storybook/cli sb init

만약 여러분의 프로젝트가 CRA로 만든 프로젝트가 아니라면? 이 링크를 읽어주세요. 직접 설정하는것도 그렇게 번거롭지는 않습니다.

그러면, 프로젝트에 .storybook 디렉터리가 생성됩니다. 생성된 config.js 를 열어보시면 다음과 같은 코드가 있습니다.

src/config.js

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

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

이 설정에서는 스토리 파일을 src/stories 경로에서만 읽고 js 확장자만 처리하고있습니다. 우리는 TypeScript를 사용 할 것이므로, 확장자도 바꿔주고 경로도 '../src'로 바꿔주세요.

src/config.js

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

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

그 다음에, docs 애드온과 @storybook/preset-create-react-app 프리셋을 설치해주겠습니다. 이 프리셋의 용도는 CRA로 만든 프로젝트에서 docs 를 만드는 과정에서 props 의 타입들을 추출 할 때 사용하는 react-docgen-typescript-loader 를 더욱 쉽게 적용하는 용도입니다.

yarn add --dev @storybook/preset-create-react-app @storybook/addon-docs
# 또는 npm install --dev @storybook/preset-create-react-app @storybook/addon-docs

설치를 하고난 다음에는 .storybook/presets.js 파일을 만들어서 다음과 같이 입력해주세요.

.storybook/presets.js

module.exports = [
  '@storybook/addon-docs/react/preset',
  {
    name: '@storybook/preset-create-react-app',
    options: {
      tsDocgenLoaderOptions: {}
    }
  }
];

src/stories 경로는 불필요하므로 제거해주세요.

이제 간단한 컴포넌트를 만들어서 storybook 작동이 제대로 이루어지는지 확인해봅시다.

src/MyComponent.tsx

import React from 'react';

export type MyComponentProps = {
  /** 아무 이름이나 입력해보세요. */
  name: string;
};

/** 별 의미 없는 예시 컴포넌트 */
const MyComponent = ({ name }: MyComponentProps) => {
  return <div>Hello, {name}</div>;
};

MyComponent.defaultProps = {
  name: 'Storybook'
};
export default MyComponent;

src/MyComponent.stories.tsx

import React from 'react';
import MyComponent from './MyComponent';

export default {
  component: MyComponent,
  title: 'MyComponent'
};

export const myComponent = () => <MyComponent name="Storybook" />;

myComponent.story = {
  name: 'Default'
};

다 작성하셨으면, yarn storybook (또는 npm run storybook) 명령어를 실행해주세요.

image.png

결과가 잘 나타났나요?

이제, 우리가 배웠던 것들 이 프로젝트에서도 동일하게 진행을 하시면 되겠습니다~

7. 유용한 Storybook 팁

리액트 라우터를 사용 할 경우

여러분이 만약 리액트 라우터를 사용하는 컴포넌트의 스토리를 작성하게 된다면, storybook-react-router를 사용해보세요.

해당 패키지를 설치 후 다음과 같이 스토리 파일에서 이를 불러온다음에

import StoryRouter from 'storybook-react-router';

StoryRouter를 스토리 파일의 decorators 부분에 넣고 나면 내부에서 Link 컴포넌트를 통해 이동을 하게 될 떄 다음과 같은 형식으로 액션 부분에 PUSH 가 나타나게 됩니다.

image.png

예시 링크: 링크: https://diligent-hearing.surge.sh/?path=/story/notelist--basic

반응형 디자인을 하는 경우

반응형 디자인이 필요한 컴포넌트를 개발 할 때에는 @storybook/addon-viewport 애드온을 사용해보세요.

image.png

이 패키지를 설치하고 나서 .storybook/addons.js 파일에서 다음 코드를 입력하여 애드온을 적용할 수 있습니다.

import '@storybook/addon-viewport/register';

그러면, 이렇게 Storybook 화면에서 viewport를 선택 할 수 있게 됩니다.

image.png

예시 링크: http://react-uikit-sample.surge.sh/?path=/story/components-dialog--dialog

추가적으로, emotion을 사용 할 때 media query를 사용하는 방법은 이 링크를 참고해보세요.

리덕스를 사용하고, 컨테이너 컴포넌트의 스토리를 작성 할 경우

리덕스를 사용 할 때 컨테이너 컴포넌트의 스토리를 작성 할 경우, 다음과 같이 Provider 에서 mock store를 사용하는 decorator를 직접 만들어서 적용하시면 액션이 디스패치 될 때 Storybook의 Actions 탭에서 확인 할 수 있습니다.

import React from 'react';
import { action } from '@storybook/addon-actions';
import { Provider } from 'react-redux';
import Container from './Container';


// mock store
const store = {
  getState: () => {
    return {
      todos: [
        {
          id: 1,
          text: 'Do Something',
          done: false
        }
      ]
    };
  },
  subscribe: () => {},
  dispatch: action('disptach')
};

const withReduxMockStore = (story: () => JSX.Element) => (
  <Provider store={store}>{story()}</Provider>
);


export default {
  title: 'components|Container',
  component: Container,
  decorators: [withReduxMockStore]
};

export const sample = () => <Container />

강의를 마치면서..

이번 강의를 통하여 여러분들은 Storybook을 활용하는 방법을 배우셨고, Storybook의 도움을 받아 나만의 디자인 시스템을 만드는 방법도 알아보았습니다.

추가적으로, npm에 패키지를 직접 등록하는 것도 알아보았죠.

디자인 시스템을 제대로 구축하고 나면 나중에 디자인/개발 공수를 많이 줄여줄 수 있고 사용자에게 일관적인 UI를 제공 하는 것에 큰 도움을 줍니다.

하지만 디자인 시스템을 구축하는 작업 자체가, 절대 쉬운 일은 아닙니다. 재사용 가능한 컴포넌트를 제대로 만들고, 문서화도 열심히 하려면 준비하는 시간이 분명히 오래 걸립니다. 추가적으로, 이 강의에서는 빠진 프로세스이지만, 디자인 시스템에서 사용하는 컴포넌트들에 대한 테스트 코드를 작성하는 것 또한 매우 중요합니다. (테스트 코드를 작성 할 때에는 React Testing Library)를 사용 하는 것을 추천드립니다.)

디자인 시스템을 만들기 위해서는 디자이너와 개발자의 긴밀한 협업이 필요합니다. 그리고 가장 중요한것은, 작업을 시작하는 것입니다. 풀타임으로 디자인 시스템을 작업해서 멋진 디자인 시스템을 만들어낼 수 있다면 좋겠지만, 현실적으로 모든 시간을 디자인 시스템 개발에만 쏟기엔 어려울 수도 있습니다 (물론 인적자원 및 주어진 시간이 충분하다면 가능 할 것입니다). 이 작업을 미루지 않고, 조그맣게 시작을해서, 주기적으로 작업을 하고 계속해서 개선해나가는 것이 중요합니다.

규모가 큰 프로젝트에서 일관적인 UI 를 제공하면서 브랜드 이미지의 통일화 하는 작업을 하고자 한다면 디자인 시스템을 만들어서 사용하는 것은 정말 탁월한 선택입니다. 하지만, 규모가 작은 프로젝트를 작업하거나, 아니면 관리자 대시보드처럼 수많은 사용자가 아닌 내부 인원이 사용하는 프로젝트를 만드는 경우라면, 디자인 시스템을 사용하는 것은 불필요한 시간을 너무 많이 잡아먹게 되어 독이 될 가능성도 있다는 것, 염두하시길 바랍니다.

만약 디자인 시스템 개발을 할 여유가 부족하다면 꼭 디자인 시스템을 만들지 않고, 라이브러리를 만들지 않더라도, 프로젝트에 Storybook을 적용하여 프로젝트에서 사용중인 몇몇 컴포넌트에 대하여 문서화를 하는 것도 프로젝트 퀄리티 향상에 큰 도움이 됩니다.

이 강의를 준비 할 수 있도록 프로그램을 마련해준 Facebook Innovation Lab에 감사를 드립니다. 그리고, 이 강의를 검수해주신 류지환님께 감사드립니다.