[create-react-app] Storybook 사용하기

[create-react-app] Storybook 사용하기

2023-08-29 hit count image

create-react-app로 생성한 TypeScript 기반 React 프로젝트에 Storybook을 설치하고 사용하는 방법에 대해서 알아봅시다.

개요

이번 블로그 포스트에서는 create-react-app을 사용하여 TypeScript를 기반으로 하는 React 프로젝트에서 Storybook을 설치하고 사용하는 방법에 대해서 알아보겠습니다.

CRA를 사용하여 TypeScript 기반 React 프로젝트 생성

CRA(create-react-app)을 사용하여 TypeScript가 적용된 React 프로젝트에서 Storybook을 사용하기 위해, 다음 명령어를 실행하여 TypeScript가 적용된 React 프로젝트를 생성합니다.

npx create-react-app storybook_example --template=typescript

Storybook 설치

TypeScript가 적용된 React 프로젝트에서 Storybook을 사용하기 위해서는 Storybook을 설치할 필요가 있습니다. 다음 명령어를 실행하여 Storybook을 설치합니다.

# cd storybook_example
npm install --save-dev storybook

Storybook 초기화

Storybook을 사용하기 위해서는 Storybook을 초기화하여 필요한 라이브러리를 설치할 필요가 있습니다. 다음 명령어를 사용하여 Storybook을 초기화합니다.

npx storybook init

그럼 다음과 같이 ESLint에 필요한 라이브러리를 설치할 것인지 물어보는 화면이 나옵니다.

. ✓
 • Adding Storybook support to your "Create React App" based project
  ✔ Getting the correct version of 12 packages
? We have detected that you're using ESLint. Storybook provides a plugin that gives the best experience with Storybook and helps follow best practices: https://github.com/storybookjs/eslint-plugin-storybook#readme

Would you like to install it? › (Y/n)

이 블로그에서는 y를 눌러 ESLint에 필요한 라이브러리를 설치했습니다. 그럼 다음과 같이 필요한 라이브러리가 설치되며 Storybook이 자동으로 설정되는 것을 확인할 수 있습니다.

info => Serving static files from ././public at /
info => Starting manager..
info Addon-docs: using MDX2
info => Loading Webpack configuration from `node_modules/react-scripts`
info => Removing existing JavaScript and TypeScript rules.
info => Modifying Create React App rules.
info => Using default Webpack5 setup
<i> [webpack-dev-middleware] wait until bundle finished
One of your dependencies, babel-preset-react-app, is importing the
"@babel/plugin-proposal-private-property-in-object" package without
declaring it in its dependencies. This is currently working because
"@babel/plugin-proposal-private-property-in-object" is already in your
node_modules folder for unrelated reasons, but it may break at any time.

babel-preset-react-app is part of the create-react-app project, which
is not maintianed anymore. It is thus unlikely that this bug will
ever be fixed. Add "@babel/plugin-proposal-private-property-in-object" to
your devDependencies to work around this error. This will make this message
go away.

No issues found.
<i> [webpack-dev-middleware] wait until bundle finished: /runtime~main.iframe.bundle.js
<i> [webpack-dev-middleware] wait until bundle finished: /vendors-node_modules_pmmmwh_react-refresh-webpack-plugin_client_ErrorOverlayEntry_js-node_mod-e1efac.iframe.bundle.js
<i> [webpack-dev-middleware] wait until bundle finished: /main.iframe.bundle.js
No issues found.

문제없이 필요한 라이브러리가 설치되고 Storybook이 자동으로 설정되면 다음과 같이 Storybook의 튜토리얼이 실행되는 것을 확인할 수 있습니다.

Storybook V7 Storybook tour

Start your 3 minute tour를 눌러 튜코리얼을 진행해도 되지만, 이 블로그 포스트에서는 Skip tour를 눌러 튜토리얼을 스킵하여 진행하였습니다.

문제없이 Storybook의 설치가 완료되었다면 package.json 파일이 다음과 같이 수정된 것을 확인할 수 있습니다.

{
   ...
   "scripts": {
    ...
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "eslintConfig": {
    "extends": [
      ...
      "plugin:storybook/recommended"
    ]
  },
  ...
  "devDependencies": {
    "@storybook/addon-essentials": "^7.3.2",
    "@storybook/addon-interactions": "^7.3.2",
    "@storybook/addon-links": "^7.3.2",
    "@storybook/addon-onboarding": "^1.0.8",
    "@storybook/blocks": "^7.3.2",
    "@storybook/preset-create-react-app": "^7.3.2",
    "@storybook/react": "^7.3.2",
    "@storybook/react-webpack5": "^7.3.2",
    "@storybook/testing-library": "^0.2.0",
    "babel-plugin-named-exports-order": "^0.0.2",
    "eslint-plugin-storybook": "^0.6.13",
    "prop-types": "^15.8.1",
    "storybook": "^7.3.2",
    "webpack": "^5.88.2"
  }
}

또한, 다음과 같이 Storybook을 사용하는 방법을 알려주기 위해, 샘플 코드가 함께 생성되는 것을 확인할 수 있습니다.

  • ./.storybook/...: Storybook에 대한 설정 파일입니다.
  • ./src/stories/...: Storybook의 샘플 코드입니다.

Storybook 실행

Storybook을 설치하면 자동으로 Storybook의 개발 서버가 실행되며 Storybook이 자동으로 실행되는 것을 확인할 수 있습니다. 이렇게 실행된 Storybook을 종료한 후, 다시 실행하기 위해서는 다음 명령어를 실행해야 합니다.

npm run storybook

그럼 브라우저에 http://localhost:6006/이 자동으로 열리며, 다음과 같은 화면을 확인할 수 있습니다.

Storybook screen

해당 화면은 ./src/stories/Configure.mdx 파일이 표시된 화면입니다.

Storybook 확인

실행된 Storybook의 왼쪽 메뉴의 Button > Docs를 선택하면 다음과 같은 화면을 볼 수 있습니다.

Storybook button sample

해당 화면은 ./src/stories/Button.stories.ts 파일이 표시된 화면입니다.

샘플 코드 확인

좀 더 자세히 알아보기 위해 샘플 코드를 확인해 봅시다. Button 컴포넌트(./src/stories/Button.tsx)는 다음과 같습니다.

import React from 'react';
import './button.css';

interface ButtonProps {
  primary?: boolean;
  backgroundColor?: string;
  size?: 'small' | 'medium' | 'large';
  label: string;
  onClick?: () => void;
}

export const Button = ({
  primary = false,
  size = 'medium',
  backgroundColor,
  label,
  ...props
}: ButtonProps) => {
  const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
  return (
    <button
      type="button"
      className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
      style=
      {...props}
    >
      {label}
    </button>
  );
};

Button 컴포넌트는 primary, backgroundColor 등 다양한 Props를 가지고 있는 것을 확인할 수 있습니다. 다음으로 Storybook 파일(./src/stories/Button.stories.ts)의 내용을 확인해 봅시다.

import type { Meta, StoryObj } from '@storybook/react';

import { Button } from './Button';

const meta = {
  title: 'Example/Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {
    backgroundColor: { control: 'color' },
  },
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};

export const Secondary: Story = {
  args: {
    label: 'Button',
  },
};

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

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

우선, Storybook의 화면 구성에 필요한 정보를 준비합니다.

import type { Meta, StoryObj } from '@storybook/react';

import { Button } from './Button';

const meta = {
  title: 'Example/Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {
    backgroundColor: { control: 'color' },
  },
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;
...

metaStorybook에 표시할 기본 정보를 설정합니다. meta에 설정한 titleStorybook의 왼쪽 메뉴에 표시될 이름을 나타내며 /를 통해 그룹을 만들 수 있습니다. 예제 코드에서는 Example 그룹 하위에 Button이라는 이름으로 표시되는 것을 확인할 수 있습니다.

Storybook meta information

metacomponent는 이 Storybook에 표시할 컴포넌트를 지정합니다. 예제에서는 Button 컴포넌트를 지정했습니다.

tags: ['autodocs']는 자동으로 문서(Document)를 작성해주는 새로운 기능입니다. 이 값을 설정하면 위에 그림에서처럼 Example/Button 메뉴 하위에 Docs가 자동으로 생성되는 것을 확인할 수 있으며, 여기서 Storybook 파일에 작성한 내용을 문서 형태로 확인할 수 있습니다.

이렇게 화면 구성에 필요한 정보를 설정하였다면, 이제 실제로 화면에 표시될 Story를 작성합니다. 다음과 같이 컴포넌트의 Props들을 설정하거나 변경하는 것으로 Story를 작성할 수 있습니다.

...
export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};

export const Secondary: Story = {
  args: {
    label: 'Button',
  },
};

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

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

이렇게 작성한 Story는 왼쪽 메뉴의 Docs 하위에 표시되는 것을 확인할 수 있습니다.

Storybook meta information

이를 통해 다른 개발자가 어떤 Props를 변경하면 어떻게 변화하는지 확인할 수 있으며, 필요한 상태의 컴포넌트를 쉽게 가져다 쓸 수 있도록 만들 수 있습니다.

완료

이번 블로그 포스트에서는 create-react-app으로 만든 TypeScript 기반 React 프로젝트에서 Storybook을 설치하고 실행하는 방법에 대해서 알아보았습니다. StorybookReact 프로젝트에서 컴포넌트를 개발하고 테스트하는데 매우 유용한 도구입니다. 또한 Storybook을 활용하면 React 프로젝트에서 CDD(Component Driven Development)를 할 수 있으므로, 여러분도 자신의 프로젝트에 넣어서 사용해 보시기 바랍니다.

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

앱 홍보

책 홍보

스무디 한 잔 마시며 끝내는 React Native 책을 출판한지 벌써 2년이 다되었네요.
이번에도 좋은 기회가 있어서 스무디 한 잔 마시며 끝내는 리액트 + TDD 책을 출판하게 되었습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.

스무디 한 잔 마시며 끝내는 React Native, 비제이퍼블릭
스무디 한 잔 마시며 끝내는 리액트 + TDD, 비제이퍼블릭
[심통]현장에서 바로 써먹는 리액트 with 타입스크립트 : 리액트와 스토리북으로 배우는 컴포넌트 주도 개발, 심통
Posts