[Next.js] Next.js에 Storybook 설치하기

김방울·2024년 5월 2일
0

Next.js

목록 보기
6/6
post-thumbnail

최근 사이드 프로젝트를 진행하면서 UI 컴포넌트를 독립적으로 개발하는 것이 좋을 것 같다는 생각이 들어, Storybook을 도입해보았습니다. 설정 과정에서 우여곡절을 겪었는데 후에 같은 시행착오를 반복하지 않도록 설치과정을 정리해 보고자 합니다.🤯

패키지 설치

npx storybook@latest init
yarn dlx storybook@latest init(yarn을 사용할 경우)


설치가 완료될 경우 루트 폴더에 .storybook 폴더가, src 폴더 내에는 .stories 폴더가 생성됩니다. (src 폴더를 사용하지 않을 시에는 root 폴더에 생성됩니다)

설치 완료 후 http://localhost:6006/ 으로 접속하면 storybook이 정상 실행되는 것을 확인할 수 있습니다.😎

storybook 실행

npm run storybook
yarn storybook(yarn을 사용할 경우)

storybook은 next.js나 react와는 별도로 실행되며, 기본적으로 6006 포트에서 실행됩니다.

실행 포트를 변경하고 싶다면 package.json"storybook" 부분을 변경하면 됩니다.(storybook dev -p [포트번호])

Styled-components 설정

styled-components를 사용하는 경우, 일반적으로 GlobalStyleThemeProvider를 이용하는 경우가 많습니다. 이는 추가적으로 preview.ts에 설정해 주어야 합니다.

설치 직후 preview.ts 파일은 다음과 같습니다.

// .storybook/preview.ts
import type { Preview } from '@storybook/react';

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
};

export default preview;


GlobalStyle이 적용되지 않아 앙상한 모습입니다. 또한 theme prop을 이용했다면 cannot read properties of undefined (reading '~~') 오류가 발생합니다. ThemeProvider를 layout.tsx에 적용했던 것처럼, preview.ts에도 적용해 주어야 합니다.

// .storybook/preview.tsx
import React from 'react';
import { ThemeProvider } from 'styled-components';
import type { Preview } from '@storybook/react';
import GlobalStyles from '../src/styles/GlobalStyle';
import theme from '../src/styles/theme';

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
};

export const decorators = [
  Story => (
    <>
      <ThemeProvider theme={theme}>
        <GlobalStyles />
        <Story />
      </ThemeProvider>
    </>
  ),
];

export default preview;
  • preview.tspreview.tsx로 변경해 줍니다.
  • React를 불러옵니다.(import React from 'react')
  • ThemeProvider와 적용할 테마, GlobalStyle을 불러옵니다.
  • 데코레이터를 이용하여 스토리를 래핑해 줍니다. (export const decorators)

데코레이터는 추가 "렌더링" 기능으로 스토리를 래핑하는 방법입니다. 많은 애드온은 추가 렌더링으로 스토리를 강화하거나 스토리 렌더링 방법에 대한 세부 정보를 수집하기 위해 데코레이터를 정의합니다. 스토리를 작성할 때 데코레이터는 일반적으로 추가 마크업이나 컨텍스트 목업으로 스토리를 래핑하는 데 사용됩니다.

설정 후, GlobalStyle과 theme props가 정상적으로 적용된 것을 볼 수 있습니다.

preview.ts에 전역적으로 적용하여도 되지만 각각의 스토리에 개별적으로 적용할 수도 있습니다.

// YourComponent.stories.ts|tsx
import type { Meta } from '@storybook/react';

import { YourComponent } from './YourComponent';

const meta: Meta<typeof YourComponent> = {
  component: YourComponent,
  decorators: [
    (Story) => (
      <div style={{ margin: '3em' }}>
        {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it  */}
        <Story />
      </div>
    ),
  ],
};

export default meta;

SVG 설정

컴포넌트 내 svg를 사용한 이미지나 아이콘이 있을 경우, 다음과 같은 오류가 발생하면서 렌더링이 되지 않습니다.

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  // svg to component
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: ['@svgr/webpack'],
    });
    return config;
  },
};

export default nextConfig;

next.config.mjs 에서 웹팩 로더 설정을 해 주었던 것처럼, storybook에도 svg 로더 설정을 해 주어야 합니다.

npm i -D @svgr/webpack
yarn add @svgr/webpack --dev(yarn을 사용할 경우)

svg 로더인 @svgr/webpack가 없을 경우 위 명령어를 통해 로더를 설치해 줍니다.

//.storybook/main.ts
import type { StorybookConfig } from '@storybook/nextjs';

const config: StorybookConfig = {
  stories: [
    '../stories/**/*.mdx',
    '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
  ],
  addons: [
    '@storybook/addon-onboarding',
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@chromatic-com/storybook',
    '@storybook/addon-interactions',
  ],
  framework: {
    name: '@storybook/nextjs',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
  staticDirs: ['..\\public'],
  // webpackFinal 추가
  webpackFinal: async config => {
    const imageRule = config.module?.rules?.find(rule => {
      const test = (rule as { test: RegExp }).test;

      if (!test) {
        return false;
      }

      return test.test('.svg');
    }) as { [key: string]: any };

    imageRule.exclude = /\.svg$/;

    config.module?.rules?.push({
      test: /\.svg$/,
      use: ['@svgr/webpack'],
    });

    return config;
  },
};
export default config;

storybook의 설정 파일인 main.ts의 config 객체 안에 webpackFinal 속성을 추가하고 svg일 경우 @svgr/webpack 로더를 사용하도록 설정을 추가해 주면 정상적으로 svg를 렌더링할 수 있습니다.👀

참고자료

profile
코딩하는 고양이🐱 / UI Developer, Front-end Developer

0개의 댓글