[Next.js] Storybook V7

[Next.js] Storybook V7

2023-05-29 hit count image

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

개요

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

이번 블로그 포스트에서는 StorybookV7 버전(next)을 사용하는 방법에 대해서 알아볼 예정입니다. V6 버전을 사용하는 방법에 대해서는 다음 링크를 참고하시기 바랍니다.

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

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

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

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

Storybook 설치

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

# cd start-storybook-v7
npm install --save-dev [email protected]

Storybook 초기화

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

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

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

 • Detecting project type. ✓
 • Adding Storybook support to your "Next" app

added 318 packages, and audited 1125 packages in 54s

226 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
. ✓
 • Preparing to install dependencies. ✓

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

? Do you want to run the 'eslintPlugin' migration on your project? › (Y/n)

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

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

Storybook V7 configuration automatically
{
   ...
   "scripts": {
    ...
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  ...
  "devDependencies": {
    "@storybook/addon-essentials": "^7.0.0-rc.8",
    "@storybook/addon-interactions": "^7.0.0-rc.8",
    "@storybook/addon-links": "^7.0.0-rc.8",
    "@storybook/blocks": "^7.0.0-rc.8",
    "@storybook/nextjs": "^7.0.0-rc.8",
    "@storybook/react": "^7.0.0-rc.8",
    "@storybook/testing-library": "^0.0.14-next.1",
    "eslint-plugin-storybook": "^0.6.11",
    "storybook": "^7.0.0-rc.8"
    ...
  }
}

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

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

ESLint를 사용하도록 설정하였으므로, .eslintrc.json 파일을 열면 다음과 같이 변경된 것을 확인할 수 있습니다.

{
  "extends": [
    "next/core-web-vitals",
    "plugin:storybook/recommended"
  ]
}

public 폴더 설정

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

import type { StorybookConfig } from '@storybook/nextjs'
const config: StorybookConfig = {
  ...
  docs: {
    autodocs: 'tag',
  },
  staticDirs: ['../public'],
}
export default config

Storybook 실행

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

npm run storybook

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

Storybook V7 first screen

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

Storybook 확인

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

Storybook V7 button sample

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

샘플 코드 확인

좀 더 자세히 알아보기 위해 샘플 코드를 확인해 봅시다. 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) => {
  const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
  return (
    <button
      type="button"
      className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
      {...props}
    >
      {label}
      <style jsx>{`
        button {
          background-color: ${backgroundColor};
        }
      `}</style>
    </button>
  );
};

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

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

import { Button } from './Button';

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

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

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: Meta<typeof Button> = {
  title: 'Example/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    backgroundColor: {
      control: 'color',
    },
  },
};

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

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

Storybook V7 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',
  },
};

Storybook 설정

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

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

import type { StorybookConfig } from '@storybook/nextjs';
const config: StorybookConfig = {
  stories: [
    '../@(stories|components)/**/*.mdx',
    '../@(stories|components)/**/*.stories.@(js|jsx|ts|tsx)',
  ],
  ...
};
...

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

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

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

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

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

...
import { SampleButton } from '.';

const meta: Meta<typeof SampleButton> = {
  title: 'Sample/Button',
  component: SampleButton,
  ...
};

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

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

Storybook V7 SampleButton

완료

이번 블로그 포스트에서는 TypeScript를 기반으로 하는 Next.js 프로젝트에서 컴포넌트 주도 개발을 하기 위해, Storybook V7을 설정하는 방법에 대해서 알아보았습니다. 이전 버전에 비해 많이 편리해지고 좀 더 직관적으로 변경된 것을 확인할 수 있었습니다. 이전 버전의 Storybook을 설정하고 사용하는 방법에 대해서는 다음 링크를 참고하시기 바랍니다.

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

앱 홍보

책 홍보

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

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

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