TIL 122 - Storybook 9 트러블슈팅🛠️

김영현·2025년 5월 31일
0

TIL

목록 보기
132/132

Next.js v15 App Router 기반 프로젝트에서 Storybook v9을 사용하며 겪은 문제들과 해결 방법입니다!

참고사항

storybook 세팅은 아래와 같습니다.

import type { StorybookConfig } from '@storybook/nextjs-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  addons: [
    '@storybook/addon-onboarding',
    '@chromatic-com/storybook',
    '@storybook/addon-vitest',
    '@storybook/addon-docs',
  ],
  framework: {
    name: '@storybook/nextjs-vite',
    options: {},
  },
  features: {
    experimentalRSC: true,
  },
  staticDirs: ['../public'],
};
export default config;

vitestnextjs-vite를 사용중입니다.

1. 캐시 문제(with Vitest)

//vitest.workspace.ts
import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { defineWorkspace } from 'vitest/config';

import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';

const dirname =
  typeof __dirname !== 'undefined'
    ? __dirname
    : path.dirname(fileURLToPath(import.meta.url));

// More info at: https://storybook.js.org/docs/writing-tests/test-addon
export default defineWorkspace([
  './vitest.config.mts',
  {
    plugins: [
      // The plugin will run tests for the stories defined in your Storybook config
      // See options at: https://storybook.js.org/docs/writing-tests/test-addon#storybooktest
      storybookTest({ configDir: path.join(dirname, '.storybook') }),
    ],
    test: {
      name: 'storybook',
      browser: {
        enabled: true,
        headless: true,
        provider: 'playwright',
        instances: [{ browser: 'chromium' }],
      },
      setupFiles: ['.storybook/vitest.setup.ts'],
    },
  },
]);

//vitest.config.mts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  plugins: [tsconfigPaths(), react()], //경로 인식용 플러그인도 추가하였음.
  test: {
    environment: 'jsdom',
  },
});

위 설정을 기반으로 vitest동작시 기존 테스트파일 + storybook기반으로 작성된 스토리들을 테스트합니다.

현상

Storybook을 실행하지 않은 상태에서 vitest를 돌리면 이전 상태의 설정이나 코드로 테스트가 동작하는 경우가 있음. => 테스트가 실패함.
특히 tsconfigPaths()플러그인을 넣었음에도 경로 별칭을 인식하지 못하는 경우가 있음.

원인(추측)

Storybook은 내부적으로 .storybook내부 설정 및 기타 환경 정보를 캐시하여 사용하는 것 같음.
@storybook/addon-vitest 같은 연동 또한 이 캐시를 사용하여 최신 설정이 반영되지 않은 상태에서 vitest가 실행되는듯 함.

해결방법

npm run storybook명령어로 스토리북 캐시 최신화 후, npm run test로 vitest 동작.
npm run test --no-cache로 캐시 없이 테스트 동작도 가능.


2. staticDirs 경로문제(github 빈 폴더)

//.storybook/main.ts
const config: StorybookConfig = {
  ...
  staticDirs: ['..\\public'], //<<<<이부분
};

위 설정.

현상

ubuntu환경에서 CI중 에러발생

혹시 몰라서 ..\\public였던 경로를 ./public으로 바꾸어도 똑같은 오류 발생.

원인

자세히 살펴보니 remote origin에 public폴더가 존재하지 않아서, CI도중 오류가 발생했던 것.


public폴더 내부에 파일이 존재해서 폴더가 업로드된 브랜치.

public 폴더 내부 파일이 없어서 폴더가 사라진 브랜치

해결 방법

내부에 아무 파일이나 만들어서 push

git은 내부파일이 없으면 추적하지 않는다.


3. storybook에서 RSC 렌더링

현상

async가 붙은 컴포넌트를 storybook에서 렌더링하려하니 오류 발생.

원인

storybook은 모든 컴포넌트를 클라이언트측에서 렌더링함.

해결 방법

storybook 공식문서에 나온 방법들로 해결.

//.storybook/main.ts
const config: StorybookConfig = {
  ...
  features: {
    experimentalRSC: true, //이부분
  },
};
export default config;

//page.stories.tsx (async 컴포넌트)
const meta = {
  ...
  decorators: [rscDecorator],
} satisfies Meta<typeof FestivalDetail>;

//./storybook/decorators.tsx
export const rscDecorator: Decorator = (Story) => {
  return (
    <Suspense>
      <Story />
    </Suspense>
  );
};

RSC에 대한 실험적 기능을 켠 뒤, <Suspense>컴포넌트를 데코레이터로 사용한다.


4. Layout 적용시 storybook 멈춤

현상

Next.js의 RootLayot을 Storybook decorator로 적용시 storybook ui가 멈춤.

원인(추측)

//app/layout.tsx
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <div className="min-w-screen min-h-screen flex justify-center items-center">
          {children}
        </div>
      </body>
    </html>
  );
}

RootLayout은 <html>, <body>같은 최상위 태그를 포함.
해당 최상위태그를 storybook내부에서 표현하려니 DOM충돌이나 오류가 발생하는 듯 함.

해결 방법

<html>, <body>같은 최상위 태그를 storybook레이아웃에서 제거


//app/layout.tsx
export function MainLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <div
      className={`${geistSans.variable} ${geistMono.variable} antialiased min-w-screen min-h-screen flex justify-center items-center`}
    >
      {children}
    </div>
  );
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <MainLayout>{children}</MainLayout>
      </body>
    </html>
  );
}

//.storybook/preview.ts
const preview: Preview = {
  ...
  decorators: [withMainLayout], //모든 스토리에 적용되게 preview로 적용.
};

export default preview;

정상작동한다.

profile
모르는 것을 모른다고 하기

0개의 댓글