StoryBook

김서현·2023년 5월 31일
4

프론트엔드

목록 보기
4/4
post-thumbnail

Storybook이란?

React, Angular, Vue 등의 분리된 UI 컴포넌트를 체계적이고 효율적으로 구축할 수 있는 UI 컴포넌트 개발 도구다. UI 컴포넌트 라이브러리의 문서화(documentation)를 위해 사용할 수도 있고 디자인 시스템(Design system)을 개발하기 위한 플랫폼으로 사용할 수도 있다.

스토리북의 기본 구성 단위 = 스토리(Story)
👉 하나의 UI 컴포넌트는 보통 하나 이상의 Story를 가지게 된다.
👉 각 Story는 해당 UI 컴포넌트가 어떻게 사용될 수 있는지를 보여주는 하나의 예시!

스토리북을 사용하면 UI 컴포넌트가 각각 독립적으로 어떻게 실제로 랜더링되는지 직접 시각적으로 테스트하면서 개발을 진행할 수 있다. 해당 UI 라이브러리를 사용하는 개발자 입장에서도 코드를 보지 않고도 미리 각 UI 컴포넌트를 체험해보고 사용할 수 있어서 매우 유용하다.

특히 Mockup 상태를 넣어줄 수 있어 상태 값에 따라 변경되는 디자인을 바로 확인할 수 있다.

그래서 왜 사용하는데?

컴포넌트 기반 개발

  • 기존에 스타일 작업을 할 때는 여러 상태에 따라 달라지는 컴포넌트들을 매번 확인하기 번거로웠다! 이 불편함을 해결해준다
    ex) 로그인을 한 상태에서만 보이는 아이콘, 로딩중일 때, hover했을 때, focus했을 때, input값이 유효할 때 등등 ..

  • 다른 사람의 코드를 이해하는데 드는 노력을 줄일 수 있다.

  • 자연스럽게 컴포넌트 단위로 생각하면서 개발을 진행할 수 있다.

  • 독립적인 환경에서의 UI 컴포넌트 개발 (개발하는 웹과 따로 서버를 킬 수 있기 때문!)

  • 컴포넌트 재사용 ⬆

참고 : Storybook은 UI 컴포넌트를 위한 놀이터
이 자료에서 특히 UI 컴포넌트를 위한 놀이터 부분을 읽어보면 아! 이래서 스토리북 쓰는구나 하고 확 체감이 되는 것 같다. 그래서 읽어보기를 추천한다!!

Storybook 설치

▶ 설치

yarn add global @storybook/cli
npx sb init // 필요한 의존성을 자동 설치해주고, package.json에 scripts 실행 및 빌드 명령어도 추가해준다.

🔽 선택 : 스토리북을 실행했을 때 컴포넌트 테이블을 만들어줌. 컴포넌트의 props, type, 주석 등이 적혀 있어서 개발할 때 아주 유용함.

yarn add --dev react-docgen-typescript-loader 

▶ 실행

yarn storybook // 6006 포트

Storybook 폴더구조


각 파일이 뭘 하는지 알아보자!

  • .storybook : Storybook 설정 파일들
  • .storybook/main.ts : stories를 위한 config 설정, 스토리북 자체의 구성 파일 (addon도 여기서 관리!)
import type { StorybookConfig } from "@storybook/react-vite";
const config: StorybookConfig = {
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
  ],
  framework: {
    name: "@storybook/react-vite",
    options: {},
  },
  docs: {
    autodocs: "tag",
  },
};
export default config;

addon
Storybook의 plugin 시스템. 공식 지원 addon 외에도 여러 오픈소스 addon을 register하여 기본 기능에 추가 기능을 붙일 수 있다.

반응형 개발을 위한 Viewport도 있으며, 라우팅을 위한 react-router도 존재한다.
아래서 다룰 args의 변화로 실시간으로 컴포넌트를 수정하는 것은 공식적으로 storybook에서 지원하는 기본 contorls addon이다!

  • .storybook/preview.ts : 모든 story들에 글로벌하게 적용될 포맷 세팅, 사용자가 작성하는 스토리의 구성 파일 (ex. GlobalStyle, ThemeProvider ...)
import type { Preview } from "@storybook/react";

const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: "^on[A-Z].*" },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
};

export default preview;
  • src/stories : Storybook 예제 컴포넌트들

Story 작성하기

기본꼴

<컴포넌트이름>.stories.js

export default {
  title: //스토리북에 올릴 component폴더 계층 구조,
  component: // 스토리를 만들 컴포넌트 이름
}

export const 스토리이름 = () => 해당스토리에서 테스트할 인자가 담긴 컴포넌트

속성

title : 스토리북에 올릴 component 폴더 계층 구조

  • ex) title: 'Example/Button은 Example 그룹의 Button 스토리

component : 스토리를 만들 컴포넌트 이름
args : 모든 스토리에 공통으로 전달될 props.
argTypes : 각 Story args의 행동 방식 설정.

  • ex) backgroundColor: {control: 'color'} => controls에서 선택한 컬러를 컴포넌트의 props로 전달하겠다는 의미

    controls
    개발자가 코드를 따로 변경하지 않고 Storybook에서 arguments를 동적으로 바꿔가며 인터렉션할 수 있도록 도와주는 기능 (ex. color, 텍스트 등 바꿔보기)

decorators : Story를 감싸 추가적인 렌더링 가능한 기능
parameters : Story에 대한 정적 메타 데이터 정의
excludeStories : 렌더링 제외 설정
actions : 클릭이 될 때 Storybook UI의 actions 패널에 나타날 콜백을 생성함으로써 버튼 클릭이 성공적이었는지 테스트 UI에서 확인할 수 있다.

이 외 참고 : How to write stories

예제

버튼을 만들어보자
📑 Input.tsx
먼저 대부분이 잘 알고 있는 리액트 컴포넌트를 만드는 것과 똑같다.

import React from 'react';
import { string, bool } from 'prop-types';
import './Input.css';

const Input = React.forwardRef(function Input(
  { id, label, placeholder, readonly, disabled, error, ...others },
  ref
) {
  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input
        id={id}
        type='text'
        placeholder={placeholder}
        readOnly={readonly}
        disabled={disabled}
        {...others}
        ref={ref}
      />
      {error && <div class='error'>{error}</div>}
    </div>
  );
});

export default Input;

Input.propTypes = {
  /** 아이디* */
  id: string.isRequired,
  /** 레이블* */
  label: string.isRequired,
  /** 플레이스홀더 */
  placeholder: string,
  /** 읽기전용 상태 */
  readonly: bool,
  /** 비활성 상태 */
  disabled: bool,
  /** 에러 메시지 */
  error: string
};

📑 Input.css
위에서 import한 css 파일이다.

label {
  display: inline-block;
  margin-bottom: 0.5rem;
}

input {
  display: block;
  width: 100%;
  padding: 0.375rem 0.75rem;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
  color: #212529;
  background-color: #fff;
  background-clip: padding-box;
  border: 1px solid #ced4da;
  appearance: none;
  border-radius: 0.25rem;
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

.error {
  margin-top: 5px;
  font-size: 15px;
  color: #e52528;
}

📑 Input.stories.jsx
스토리를 작성해보면,

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

export default {
  title: 'components/Form/Input',
  component: Input
};

// 기본 포맷
const Template = args => <Input {...args} />;

// bind로 제어
// 이 아래 각각이 새로운 스토리들인 것!
export const Password = Template.bind({});
Password.args = {
  id: 'password',
  label: '패스워드',
  type: 'password',
  value: '1234',
  placeholder: '패스워드를 입력하세요.'
};
Password.storyName = 'Password';

export const UserIdError = Template.bind({});
UserIdError.args = {
  id: 'userid',
  label: '아이디',
  value: 'user@email.',
  placeholder: '아이디로 사용할 이메일을 입력하세요.',
  error: '아이디(이메일)는 이메일 형식으로 입력해주세요.'
};
UserIdError.storyName = 'Password(Error)';

export const Large = {
  args: {
    size: 'large',
    label: 'Button',
  },
};

export const Small = {
  args: {
    size: 'small',
    label: 'Button',
  },
};

그 결과! 아래처럼 Story들이 생겨난다

Password

Password(Error)

이렇게나 편하게 여러가지 UI에 대한 테스트가 가능하다니.. 너무 멋지다!!

단점

그러나 많은 것을 고려하면서 개발을 하다보니 개발의 속도도 느려지고, 스토리북이라는 새로운 툴을 도입함으로써 세팅과 이슈를 해결하는데 시간을 더 투자하게 된다.
그래서! 토이 프로젝트나 규모가 작은 프로젝트의 경우에는 이점이 별로 없다고 할 수 있겠다.

참고자료
공식 웹사이트
kakao FE 기술블로그 - 스토리북 작성을 통해 얻게 되는 리팩토링 효과

1개의 댓글

comment-user-thumbnail
2023년 6월 1일

프로젝트 도중 여러 조건에서 렌더링되는 컴포넌트를 구현할 때 테스트에 많은 시간을 쏟았던 경험이 있는데 이렇게 설명해주시니 스토리북을 왜 써야하는지 바로 이해가 됩니다! 특히 최근에 대화형 상태나 앱 컨텍스트에 UI가 얽혀있거나 API연결 전 더미데이터를 하나하나 넣어가며 테스트하는 것에 지쳐있었는데 독립적으로 UI를 구축하고 테스트해볼 수 있다는 것은 큰 장점같습니다.
저는 스토리북에 사용법이 궁금해서 조금 더 찾아보았는데요!
반복되는 컴포넌트의 경우 template화 하여 사용할 수 있어서 코드를 조금 더 간결하게 짤 수 있을 것 같습니다. 좋은 글 잘 읽고 갑니다!

답글 달기