React

Storybook 도입 (1) - 마이구미

mygumi 2019. 12. 10. 23:19
반응형
이 글은 Storybook 적용해본 첫번째 후기를 다뤄본다.
첫번째라는 건, 다양한 용도, 환경 등에서 오는 다양한 후기가 있을거라 판단했다.
정확히는 본인은 계속 사용할 것이고, 굉장히 만족하여 추후에도 분명 다음 편을 쓸거라고 생각한다.
https://github.com/storybookjs/storybook

 

스토리북 설치 및 사용을 중점으로 다루지는 않을 것이다.

이번에 1차적으로 도입해본 과정을 토대로 다뤄본다.

팀 전체가 아닌 개인적으로 시도해본 것이라, 팀 단위의 도입 측면에서의 많은 정보는 가져갈 수 없을 것이다.

"이런게 있구나" 맛보기 관점에서 보았으면 한다.

 

다음과 같은 순으로 진행한다.

 

  • Storybook 은 무엇인가?
  • 본인이 StoryBook 을 사용한 이유는 무엇인가? 
  • StoryBook 을 어떻게 사용했는가?

 

Storybook 은 무엇인가?

스토리북을 정의하는 의미는 다음과 같다.

 

Storybook runs outside of your app. This allows you to develop UI components in isolation, which can improve component reuse, testability, and development speed. You can build quickly without having to worry about application-specific dependencies.

 

  • UI 컴포넌트를 독립적으로 개발함으로써, 컴포넌트 재사용성, 테스트, 개발 속도를 향상시킨다.
  • 앱은 의존성을 걱정하지 않고 빌드할 수 있다.

 

사실상 컴포넌트는 외부 영향을 받지 않고 독립적으로 동작할 수 있도록 개발되는 것이 일반적이다.

하지만 시스템이 복잡해지다보면, 그렇지 못한 경우가 많이 발생한다.

이를 위해 스토리북은 컴포넌트 단위의 개발에 조금 더 집중된 환경을 주는 것이 스토리북이다.

스토리북은 앱과는 별도의 고립된 환경에서 실행된다.

 

본인이 StoryBook 을 사용한 이유는 무엇인가? 

스토리북은 일반적인 예로는 "디자인 시스템", "스타일 가이드" 같은 느낌을 예로 들 수 있다. 

제공해주는 예제들을 보면, 어떤 느낌인지 알 수 있다. (https://storybook.js.org/docs/examples/)

 

위와 같은 예제들은 당장 도입하기가 어렵고, 많은 공수가 든다.

개발자뿐만 아니라, 디자인팀의 도움도 필요할 것이다.

결과적으로 Button, Card, ConfirmBox 등과 같은 스타일 관련 컴포넌트가 아닌,

우선 기능적인 요소를 포함하는 업로드, 네비게이션 가드, 유튜브 등과 같은 재사용 컴포넌트들에 대한 문서화를 남기고 싶었다.

 

 

위처럼 컴포넌트의 prop 에 따른 결과를 확인할 수 있다. (@storybook/addon-knobs/register 적용, 하단 참고)

knob 애드온을 사용하면 props 을 제어하면서 상태에 따른 결과를 바로 볼 수 있다.

 

더 나아가 상태에 따른 뷰 단위를 쉽게 보고 싶었다.

예를 들면, 어떤 뷰를 보기 위해서는 그 과정을 해주어야하는 번거로움 없이 이러한 과정을 스킵하는 것이다.

예를 들어, 업로드 영역의 드래그 했을 때의 뷰를 보고 싶으면, 우리가 직접 드래그를 해보아야한다.

드래그 상태로 만들어 놓으면, 그러한 과정을 스킵한 채 볼 수 있다.

 

 

위처럼 드래그 한 상태를 나타내는 모습이다.

하단 Story 탭에서 해당 코드를 확인할 수도 있다. (@storybook/addon-storysource/register 사용, 하단 참고)

결과적으로 컴포넌트를 수정할 경우, 원하는 상태들에 대한 것들을 셋팅해놓으면 시각적으로 확인하기 편하다.

 

StoryBook 을 어떻게 사용했는가?

create-react-app 을 기준으로 한다.

기본적으로 스토리북을 설치하면 다음과 같은 디렉토리가 생성된다.

 

.storybook
src/stories

 

 

.storybook 은 스토리북을 위한 커스텀 설정 관련 파일들이 존재한다.

 

  • addons.js
  • config.js

 

addons.js 는 스토리북의 많은 기능을 추가적으로 사용할 때, 각 기능을 import 하는 파일이라고 보면 된다.

config.js 는 스토리북 설정 옵션이라고 보면 된다.

 

configure(require.context('../src/stories', true, /\.stories\.js$/), module);

 

기본적으로 작성되어있는 코드로 인해, stories.js 파일들을 모두 알아서 찾아 스토리북에 뿌려주게 된다.

그 외의 보이지 않은 옵션들은 디폴트로 갖는 값을 그대로 사용해도, 큰 문제가 없다. 디폴트 값은 문서를 통해 한번 훑어보길 바란다.

(https://storybook.js.org/docs/configurations/options-parameter/)

그리고 웹팩을 위한 webpack.config.js 파일이 존재할 수 있다.

이유는 스토리북은 처음에 언급했듯이, 실제 앱 환경과는 다른 별도의 고립된 환경이기에, 웹팩 설정이 필요할 수도 있다.

기존 프로젝트에서 웹팩을 오버라이딩해서 사용하고 있는 것들이 있었다.

그래서 본인은 실제로 필요했고, 특정 애드온을 위해서도 필요했다.

 

stories 디렉토리에는 원하는 스토리를 추가적으로 구현하게 될 것이다.

ex) 0-Welcome.stories.js, 1-Button.stories.js, 2-Modal.stories.js ....

 

사실상 설치한 후에는 stories 파일을 수정 및 추가해주는 게 끝이다.

손이 가는건 조금 더 효율적이고 원하는 방식대로 사용하기 위해 애드온이나 옵션들을 커스텀하는 것이다.

본인이 기본적으로 원했던 것과 필요했던 건 다음과 같다.

 

  • 카테고리
  • props 제어
  • 해당 스토리에 대한 소스 코드 노출
  • 네비게이션 가드를 위한 router 

 

- 카테고리

카테고리는 말 그대로 카테고리를 의미한다.

기본적으로 사용하면 다음과 같이 뎁스 없이 수직으로 내려온다.

 

 

본인은 카테고리로 한번더 의미하고 싶었기에, 다음과 같이 적용하였다.

 

 

위와 같이 적용하고 싶으면, 각 스토리 파일에서 스토리를 생성할 때 다음과 같이 작성하면 된다.

 

storiesOf('UI|Welcome', module).add('', () => {...})
storiesOf('Component|ConfirmBox', module).add('', () => {...})
storiesOf('Component|RouteLeavingGuard', module).add('', () => {...})
storiesOf('Component|Loading', module).add('', () => {...})
storiesOf('Component|YoutubePlayer', module).add('', () => {...})
storiesOf('Others|Button', module).add('', () => {...})

 

이건 스토리북 옵션의 {hierarchySeparator, hierarchyRootSeparator} 디폴트된 값으로 사용할 수 있는 방법이다.

UI, Component 의 노출 순서를 고려하려면, storySort 을 커스텀하면 된다.

디폴트는 알파벳순이기에, 숫자를 활용할 수 있다.

 

0-UI.stories.js
1-Component.stories.js
2-Others.stories.js

 

- props 제어

컴포넌트의 props 제어를 통해 상태 변화를 체크하는 것을 의미한다.

이를 위해 knobs 애드온을 통해 가능하다.

https://github.com/storybookjs/storybook/tree/master/addons/knobs

 

import { storiesOf } from '@storybook/react';
import { withKnobs } from '@storybook/addon-knobs';

storiesOf('Component|ConfirmBox', module)
  .addDecorator(withKnobs)
  .add('', () => {...})

 

- 해당 스토리에 대한 소스 코드 노출 및 포커스

스토리 클릭 시 이에 해당하는 코드를 볼 수 있는 것을 의미한다.

이는 storysource 애드온을 통해 가능하다.

https://github.com/storybookjs/storybook/tree/master/addons/storysource

 

 

- 네비게이션 가드를 위한 router 

네비게이션 가드와 같이 라우트를 통한 로직이 필요한 경우를 위한 테스트 환경을 의미한다.

 

import { createMemoryHistory } from 'history';
storiesOf('Component|RouteLeavingGuard', module)
  .addDecorator(withKnobs)
  .addDecorator(story => (
    <Router history={createMemoryHistory({ initialEntries: ['/'] })}>
      <Route path="/" component={() => story()} />
    </Router>
  ))

 

1차적으로 스토리북을 이정도 수준으로 구축하여, 몇몇 컴포넌트들을 넣어보았다.

본인은 팀 전체가 아닌 개인적으로 적용했음에도, 굉장히 좋은 느낌과 방향을 가져가게 되었다.

컴포넌트를 리팩토링하는 계기가 되었고, 전체적으로 재사용성을 더 고려하고 높일 수 있었다.

이 과정에서 현재 만들어진 것들이 가지는 단점들로 인해, 추후에 어떻게 개발을 해야 효율적인지 파악할 수 있었다.

팀 단위가 되면 더 큰 장점들이 존재할거라고 보여진다.

 

이 밖에도 많은 애드온들이 존재하고, 다양한 용도로 더욱 좋은 환경을 갖출 수 있다.

글에서 다루지는 않았지만, 문서화에 더 도움주는 docs 애드온을 활용하면 더욱 좋지 않을까한다.

https://github.com/storybookjs/storybook/tree/master/addons/docs

 

반응형