[Storybook] StoryBook을 이용한 컴포넌트 단위 개발 - 기본세팅(CRA + Redux + styled-Component)

황연욱·2020년 9월 27일
10

Storybook

목록 보기
2/2
post-thumbnail

기본 세팅(CRA + Redux + styled-Component)

CRA + Redux + styledComponent 로 구성된 프로젝트에 storybook 세팅하기
아래의 내용은 storybook 6.0.22 version을 기준으로 설명하고 있습니다.

1. Storybook 설치 및 기본세팅

  • Storybook을 프로젝트에 설치하는 방법은 Project directory로 이동해서 터미널에서 아래 명령어를 입력하시면 됩니다
npx @storybook/cli sb init
  • 설치를 하고나면 프로젝트 루트 디렉토리에 .storybook 디렉토리와 src디렉토리 하위에 stories라는 디렉토리가 생기고 package.json에 storybook 관련 dependencies, devDependencies, script명령어가 생긴것을 확인할 수 있습니다.

  • 해당하는 디렉토리가 무엇이고 각 파일이 의미하는 것이 무엇인지 확인해보겠습니다.

.storybook

  • .storybook 디렉토리에는 스토리북 관련 설정들을 setting할 수 있는 파일들이 생성되어 있습니다.
  • 현재 main.js와 preview.js파일이 있습니다.
  • storybook세팅에 관련된 예제들을 찾다가보면 config.js파일을 만들어서 안에 설정내용들을 넣으라는 예시가 있는데 이는 구버전 스토리북 기준으로서 main.js와 config.js파일이 같이 공존하면은 storybook에서 에러를 발생시키니 주의하셔야 합니다.(경험담)
  • 터미널에 npm run storybook을 통해 스토리북을 구동시켜 보면 위와 같은 화면으로 구성된 것을 볼 수 있는데
  • storybook 전체의 세팅을 관리하는 파일이 main.js파일이며
  • 위의 빨간색으로 하이라이팅 한 부분이 Preview부분이라고 명칭하는데 preview.js파일은 preview부분과 관련된 설정을 할 수 있습니다.
  • 하나하나 설정을 보면서 알아보겠습니다.

main.js

  • main.js의 초기 구성은 위와 같이 되어 있습니다.
  • stories부분과 addons부분으로 나눠진 것을 볼 수 있는데
  1. stories부분은 stroybook에 표현할 컴포넌트들을 정의합니다.

    • 현재 보시면 ".stories.@js|jsx|ts|tsx" 으로 정의되있기에 src폴더 하위에 .stories가 파일명에 포함되 있으며 확장자가 js/jsx/ts/tsx로 되어있는 파일들을 모두 스토리북에 표현하겠다는 의미입니다.
    • config.js를 사용해 설정을 했던 구버전 스토리북에서는 모든 stories 파일이 ./src/stories 디렉토리에 있어야만 했고 그 외의 stories파일이 스토리북에 나오게하려면 추가적인 설정이 필요했지만 현재에는 기본설정만으로도 src 디렉토리 하위의 어떤 폴더에 있든 모두 스토리북에 표현되도록 변경되었습니다.
  2. addon 부분은 storybook에 어떤 addon을 추가시킬지 설정하는 부분입니다.

    • addon이란 것은 extension과 유사한 개념인데 기본 스토리북에 추가적인 기능을 더 추가하는 것이라고 볼 수 있습니다.
    • 하나의 예시를 들자면 컴포넌트들의 접근성을 확인할 수 있는 a11y에드온을 추가하려면
    • npm install @storybook/addon-a11y --dev 명령어를 통해서 에드온을 설치 및 devDependencies에 추가하고
    addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/preset-create-react-app",
    "@storybook/addon-a11y",
    ],
    • addons에 다음과 같이 @storybook/addon-a11y를 추가하면
    • 위와 같이 preview부분에 accessibility 탭이 추가되어서 접근성 관련된 항목들이 추가된 것을 알 수 있습니다.

2. StoryBook에 Redux + Styled-Components 적용시키기

  • StoryBook에 Redux의 Store와 Styled-Components의 theme을 적용시키기 위해서는 데코레이터를 추가시켜줘야 합니다.
  • 이 부분은 preview.js파일에 해당하는 설정을 추가함으로서 구현할 수 있습니다.

preview.js

  • preview.js에서는 preview부분과 관련된 설정을 합니다.

  • 이 부분을 이용하여 story에 데코레이터를 추가할 수 있습니다.

  • 데코레이터란 story 컴포넌트가 storybook에 표현될 때 추가적으로 장식으로 들어가는 다른 컴포넌트들이나 Provider들을 추가할 수 있는 것입니다.

  • Redux나 styled-components를 사용할 경우에는 루트가 되는 스크립트 파일인 index.js에서 Provider를 통해서 store를 전역에 추가해주고, ThemeProvider를 통해서 theme파일을 전역에 추가하는 식으로 세팅을 많이 했을 것입니다.

    Provider와 ThemeProvider가 추가된 index.js파일의 예시

  • storybook은 일반적으로 서버를 구동시킬때 index.js를 통해서 모든 컴포넌트가 Provider를 통해서 Provider를 제공받은 후 보여지는 것이 아니라

  • 개별 컴포넌트가 스토리북에 보여지는 것이므로 저런 Provider가 제공된 상태가 아니기에 theme와 store를 사용하였을 경우에 에러가 발생하거나 원하는대로 동작하지 않습니다.

  • 따라서 Decorator를 통해서 Provider를 개별 스토리에 다 제공해줘서 store와 theme을 제공받도록 해아합니다.

  • 개별 스토리마다 decorator를 추가할 수도 있지만, 모든 스토리에 공통적으로 적용되어야 하는 decorator라면 Preview.js에 전체적으로 데코레이터를 추가해주는 방식이 간편합니다.
     

    Preview.js 파일의 예시

  • 위 preview.js파일을 보면 index.js에 Provider를 제공한것과 동일하지만 중간에 Story라는 컴포넌트가 들어가 있는 것을 볼 수 있습니다.

  • 정확한 내용은 공식문서에서 해당하는 내용을 찾아봐야겠지만 제가 사용하고 세팅을 하면서 추측하기로는 decorator는 story라는 인자를 받는데 이 인자는 각 .stories파일들에서 export하는 컴포넌트들이고 그 Story파일에 데코레이터로 Provider, ThemeProvider, GlobalStyles를 감싸준 다음에 렌더시키는 방식으로 보입니다.

  • 제 경우에는 모바일 사이즈를 개발하고 잇기에 width:360px을 가진 wrapper를 전체적으로 적용시켜준 모습을 볼 수 있습니다.

  • 이 개념을 이해하면 이제 스토리북에 다양한 데코레이터를 추가해서 여러가지 기본적인 설정을 해줄 수 있습니다.

documentation 추가하기

  • Storybook에는 사용자를 위해서 docs를 추가할 수 있습니다.

  • mdx파일을 추가하는 방식과 PropTypes를 통해서 간단하게 표현해줄 수 있는 방법이 있습니다.

  • 저의 경우에는 propTypes를 통해서 간략하게 문서화 하는 식으로 진행하였습니다.

  • React에서 기본적으로 제공하는 prop-types 라이브러리를 통해서 Props의 타입을 명시할 수 있고, 이와 주석을 결합하여 간략하게 해당하는 prop의 documentation을 스토리북에 포함할 수 있습니다.

    Example.js 파일
    ---
    import React from "react";
    import styled from "styled-components";
    
    function Example({ color }) {
      return <Container>Example</Container>;
    }
    
    export default Example;
    
    const Container = styled.p`
      width: 200px;
      color: blue;
      font-size: 36px;
    `;
    Example.stories.js 파일
    ---
    import React from "react";
    import Example from "./Example";
    
    export default {
      title: "Components",
      componenet: Example,
    };
    
    export const Default = () => <Example />;
  • 위와 같은 형식으로 하나의 컴포넌트파일을 만들고 .stories.js파일을 만들어서 storybook에 컴포넌트를 표현할 수 있습니다.

  • 실제 스토리북에 들어가보면 컴포넌트의 모습은 보이지만 문서화가 되지 않아 사람들이 알아보기 어려운 것을 알 수 있습니다.

    import React from "react";
    import PropTypes from "prop-types";
    import styled from "styled-components";
    
    function Example({ color }) {
      return <Container color={color}>Example</Container>;
    }
    
    Example.defaultProps = {
      color: "black",
    };
    
    Example.propTypes = {
      /** 글자의 색을 지정하는 Props */
      color: PropTypes.string,
    };
    
    export default Example;
    
    const Container = styled.p`
      width: 200px;
      color: ${({ color }) => color};
      font-size: 36px;
    `;
  • 위와 같이 propTypes를 통해서 defaultProps와 propTypes를 지정해주면 해당하는 주석을 문서로 출력해줍니다.

3. 겪었던 Issue들

1. Stories of

  • 스토리북 파일을 작성하는 방법은 크게 두가지가 있는데 Stories of 를 사용하는 방식과, export default로 title과 component를 정의해준 다음, export const <표현하고싶은 이름> 을 통해서 하는 방식 2가지가 있는데 처음에는 Storiesof를 사용했으나 이 경우 propTypes를 통한 문서화가 표현되지 않는 이슈가 있어서 export default 와 export const를 사용하는 방식으로 변경하였습니다.
  • Stories of가 더 간결하게 느껴져서 최신 문법인 줄 알고 있었으나 export default + export const를 사용하는 방식이 더 최신문법이고 권장사항이라고 공식문서에서 소개하는 글을 보고 모두 그 방식으로 수정했습니다.

2. Optional Chainig

  • Optional Chaining문법을 쓰면 propTypes를 통한 문서화가 되지 않는 이슈를 겪었습니다.
    현재 해결방법은 못찾은 상태여서 한줄 한줄 다 지워가면서 확인하다가 옵셔널체이닝을 쓰면 문서화가 표출되지 않는 다는 것을 발견하고 현재 &&문법을 사용하는 식으로 수정했습니다.
  • 관련해서 해결하는 방법을 찾으면 추가 포스팅 하겠습니다.

3.Position:fixed

  • Storybook에서 컴포넌트를 보여주는 형태는 storybook페이지 안에 iframe을 통해서 다른 html문서를 추가하고 그 안에 컴포넌트가 있는 방식으로 보입니다.
  • 그래서 position:fixed, bottom:0이 설정되어있는 컴포넌트가 있을 경우에 docs페이지에서 docs페이지 전체의 bottom에 모든 컴포넌트가 위치해버리는 이슈가 있습니다.
    Fixed의 예시
  • 위의 사진을 보시면 docs탭 안에서 모든 컴포넌트가 다 bottom:0 에 위치해있어서 난잡하게 보이고 해당 컴포넌트의 이미지를 볼 수가 없는 상황입니다.
  • 이 경우 Component를 싸고있는 iframe의 크기를 설정해줌으로서 해결할 수 있습니다.
import React from 'react'
import Example from "./Example";

export default {
  title:"Test",
  component:Example,
  parameters: {
    docs: {
      inlineStories: false,
      story: {
        ifameHeight: 500,
      },
    },
  },
}

export const Default = (args) => <Example {...args} />
export const Blue = () => <Example color="blue" />
  • 위의 코드에서 default안에 parameters부분을 보면 docs탭에서 iframe의 height를 500으로 설정해준 것을 볼 수 있습니다.
  • 위의 코드를 추가해주면 정상적으로 docs탭 안에서 정상적인 모습으로 출력이 되는 것을 볼 수 있습니다.

마무리

  • 제가 스토리북을 처음 사용하면서 겪었던 시행착오와 Issue들을 정리해봤습니다.
  • 이 글을 통해 한분이라도 더 제가 겪었던 시행착오를 조금이라도 빨리 해결할 수 있었으면 좋겠습니다.
  • CRA만 사용하다가 처음으로 다른 라이브러리를 같이 세팅하는 과정을 겪어봤는데, 1주일 정도 걸린 것 같습니다.
  • 개발에서 초기에 세팅단계에서 많은 시간이 소모되고 세팅이 꼬일 경우 어렵다는 것을 처음으로 느낀 경험이었습니다.
  • 관련해서 storybook뿐만 아니라 babel, webpack 등 여러가지 세팅관련된 것들을 공부해서 개발환경 설정을 할 수 있는 능력을 갖춰야겠다는 생각을 했습니다.
profile
효율적이고 아름다운 코드를 쓰고싶은 호기심 많은 개발자입니다.

2개의 댓글

comment-user-thumbnail
2021년 9월 3일

잘봤습니다.
previews 에 store 를 넣어야 한다는걸
이 포스트를 보고 알았네요..
제 삽질시간을 조금 단축시켜주셔서 감사합니다

답글 달기
comment-user-thumbnail
2023년 1월 20일

public 폴더에 있는 svg 파일을 가져오는 중 에러가 나서 찾아보는데 대부분 webpack.config.js 파일을 추가하라 하더군요.. 추가하면 에러가 나고.. 덕분에 이 방법이 아닌 걸 알고 갑니다
유용한 글 감사드립니다

답글 달기