[Next.js] Storybook

2022-05-14 hit count image

TypeScript를 기반으로 한 Next.js 프로젝트에 Storybook를 사용하여 컴포넌트 주도 개발을 해 봅시다.

개요

이번 블로그 포스트에서는 TypeScript를 기반으로 하는 Next.js 프로젝트에서 Storybook를 사용하여 컴포넌트 주도 개발(Component Driven Development)을 해 봅시다.

여기서 소개한 소스코드는 아래에 링크를 통해 확인할 수 있습니다.

블로그 리스트

이 블로그 포스트는 시리즈로 제작되었습니다. 다음은 Next.js의 시리즈 리스트입니다.

TypeScript 기반 Next.js 프로젝트 생성

TypeScript가 적용된 Next.js에서 Storybook을 사용하기 위해, 다음 명령어를 실행하여 TypeScript가 적용된 Next.js 프로젝트를 생성합니다.

npx create-next-app --typescript start-storybook

Storybook 설치

TypeScript가 적용된 Next.js 프로젝트에서 Storybook을 사용하여 컴포넌트 주도 개발을 하기 위해서는 Storybook을 설치할 필요가 있습니다. 다음 명령어를 실행하여 Storybook을 설치합니다.

# cd start-storybook
npm install --save-dev sb

Storybook 초기화

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

npx sb init --builder webpack5

그럼 다음과 같이 자동으로 Storybook이 초기화 되는 것을 확인할 수 있습니다.

 sb init - the simplest way to add a Storybook to your project.

 • Detecting project type. ✓

    There seems to be a Storybook already available in this project.
    Apply following command to force:

   sb init [options] -f

🔎 checking 'cra5'
🔎 checking 'webpack5'
🔎 checking 'angular12'
🔎 checking 'mainjsFramework'
Unable to find storybook main.js config, skipping
🔎 checking 'eslintPlugin'

마지막으로 다음과 같은 질문이 나옵니다.

? Do you want to run the 'eslintPlugin' fix on your project? › (y/N)

ESLint를 사용하여 코드를 검사하는 경우 y를 눌러 진행하고, 그렇지 않은 경우는 N을 눌러 진행합니다. 이 블로그에서는 y를 눌러 진행하였습니다.

그럼 다음과 같이 Storybook이 자동으로 필요한 라이브러리를 설치하고, Storybook을 실행하는 스크립트를 자동으로 작성된 것을 확인할 수 있습니다.

Storybook configuration automatically

{
   ...
   "scripts": {
    ...
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  },
  ...
  "devDependencies": {
    "@babel/core": "^7.17.7",
    "@storybook/addon-actions": "^6.4.19",
    "@storybook/addon-essentials": "^6.4.19",
    "@storybook/addon-interactions": "^6.4.19",
    "@storybook/addon-links": "^6.4.19",
    "@storybook/builder-webpack5": "^6.5.0-beta.8",
    "@storybook/manager-webpack5": "^6.5.0-beta.8",
    "@storybook/react": "^6.4.19",
    "@storybook/testing-library": "^0.0.9",
    "babel-loader": "^8.2.3",
    "eslint-plugin-storybook": "^0.5.7",
    ...
  }
}

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

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

public 폴더 설정

Next.jspublic 폴더에 위치한 static 파일들(이미지)을 Storybook에서 인식하도록 하기 위해 scripts를 다음과 같이 수정합니다.

{
   ...
   "scripts": {
    ...
    "storybook": "start-storybook -p 6006 -s ./public",
    "build-storybook": "build-storybook -s public"
  },
  ...
}

Storybook 실행

이제 지금까지 설정한 Storybook을 실행하여, Storybook이 잘 설치되었는지 확인해 봅시다. 다음 명령어를 실행하여 Storybook을 실행합니다.

npm run storybook

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

Storybook first screen

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

Storybook 확인

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

Storybook button sample

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

샘플 코드 확인

좀 더 자세히 알아보기 위해 샘플 코드를 확인해 봅시다. Button 컴포넌트(./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) => {
  ...
};

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

import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { Button } from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
} as ComponentMeta<typeof Button>;

const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Button',
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: 'Button',
};

export const Large = Template.bind({});
Large.args = {
  size: 'large',
  label: 'Button',
};

export const Small = Template.bind({});
Small.args = {
  size: 'small',
  label: 'Button',
};

우선, Storybook에 표시할 컴포넌트를 준비합니다.

import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { Button } from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
} as ComponentMeta<typeof Button>;

const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
...

argTypesbackgroundColorcontrol: 'color'을 설정하면, 다음과 같이 Storybook에서 색상을 선택할 수 있습니다.

Storybook color control

그런 다음 화면에 표시할 Story를 작성합니다.

...
export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Button',
};
...

여기서 argsButton 컴포넌트의 Props를 설정하여 다양한 Story를 만들 수 있습니다.

Storybook 설정

우선 Next.jsglobals.cssStorybook에서도 사용할 수 있도록 설정할 필요가 있습니다. ./storybook/preview.js 파일을 열고 다음과 같이 수정합니다.

import '../styles/globals.css'

export const parameters = {
  ...
}

현재는 Storybook을 작성하기 위해서는 ./stories 폴더에 작성해야 합니다. 하지만, 보통 컴포넌트는 ./components 폴더를 생성하고 관리하게 됩니다.

이렇게 Storybook을 다른 폴더에서 작성하기 위해서는 ./.storybook/main.js 파일을 수정할 필요가 있습니다. ./.storybook/main.js 파일을 열고 다음과 같이 수정합니다.

module.exports = {
  "stories": [
    "../**/*.stories.mdx",
    "../**/*[email protected](js|jsx|ts|tsx)"
  ],
  ...
}

이제는 어떠한 폴더에서든 .stories.tsx 파일명을 가지면, Storybook이 이를 인식하여 화면에 표시하게 됩니다. 이를 확인하기 위해 ./components/SampleButton 폴더를 생성하고 다음 파일들을 복사합니다.

  • ./stories/button.css > ./components/SampleButton/index.css
  • ./stories/Button.stories.tsx > ./components/SampleButton/index.stories.tsx
  • ./stories/Button.tsx > ./components/SampleButton/index.tsx

그리고 ./components/SampleButton/index.tsx 파일을 열어서 다음과 같이 수정합니다.

...
import './index.css';
...
export const SampleButton = ({
   ...
}: ButtonProps) => {
   ...
};

그리고 ./components/SampleButton/index.stories.tsx 파일을 열어서 다음과 같이 수정합니다.

...
import { SampleButton } from '.';
...
export default {
  title: 'Example/SampleButton',
  component: SampleButton,
  ...,
} as ComponentMeta<typeof SampleButton>;

const Template: ComponentStory<typeof SampleButton> = (args) => (
  <SampleButton {...args} />
);
...

Storybook의 설정을 변경하였으므로 이를 반영하기 위해, 현재 실행중인 Storybook을 종료하고 다시 실행합니다. Storybook이 다시 실행되면 다음과 같이 우리가 만든 SampleButton이 잘 표시되는 것을 확인할 수 있습니다.

Storybook SampleButton

완료

이번 블로그 포스트에서는 TypeScript를 기반으로 하는 Next.js 프로젝트에서 컴포넌트 주도 개발을 하기 위해, Storybook을 설정하는 방법에 대해서 알아보았습니다. 이제 앱을 개발할 때, Storybook을 보면서, 컴포넌트에 집중하여 개발할 수 있게 되었습니다.

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

앱 홍보

책 홍보

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

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

스무디 한 잔 마시며 끝내는 React Native, 비제이퍼블릭
스무디 한 잔 마시며 끝내는 리액트 + TDD, 비제이퍼블릭
Posts