Storybook 적용하기

robin Han·2025년 3월 6일
2

트러블 슈팅

문제 1

No QueryClient set, use QueryClientProvider to set one

useNavigate() may be used only in the context of a <Router> component.

App.tsx 에서 설정에서는
QueryClientProvider로 감싸주어서 queryClient를 사용할수있게 해주어야 된다. 하지만 Storybook에서는 따로 감싸주는 컴포넌트가 없어서 추가적인 설정이 필요하다,

방법 1

해당 스토리를 파일안에 개별 설정을 하기, decorators를 선언해서 Story 를 필요한 컴포넌트로 감싸주는걸 반환해주는 함수를 사용하면 된다.

const meta: Meta<typeof Mycomponent> = {
  component: Mycomponent,
  ---- 추가된 부분 -------
    decorators: [
    (Story) => (
      <QueryClientProvider client={queryClient}>
    	<BrowserRouter>{Story()}</BrowserRouter>
  	  </QueryClientProvider>
    ),
  ],
-------------------------
};

방법1이 불편한 이유

방법 1 대로 모든 스토리 컴포넌트에 해당파일을 선언해주면 코드수도 늘어날뿐더러 너무 비효율적이라고 생각했다 필요/불필요 컴포넌트 상관없이 전역 스토리를 선언해 사용하는 방법이 있다고 생각했다.

방법 2

전역에서 사용하는 preview.js에 decorator 추가, 해당 preview.js는Storybook에서 전역 설정 파일로, 모든 스토리에 공통적으로 적용할 설정을 정의하는 곳이다.

  • 전역 decorators 추가 → 모든 스토리에 공통적으로 적용할 래퍼 컴포넌트 설정
  • 전역 parameters 설정 → 컨트롤 패널, 액션 로깅, 라우팅 등의 전역 설정
  • 전역 argTypes 정의 → 특정 속성의 기본 컨트롤 방식 지정
  • 전역 loaders 정의 → API 호출 등의 데이터를 스토리에 공급

따라서 여기서 decorators를 추가하면 해결될꺼라고 생각했다.
하지만, React Query는 잘 작동하지만, react router에서 계속 BrowserRouter를 받아오지 못하는 문제가 생겼다. 당연히 원인을 다 찾고 해결하면 좋겠지만 storybook에서 제공하는 add-on으로 해결하는 방법밖에 못 찾아서 다른 방법으로 해결할수있을것 같아서 방법 3을 시도 해봤다

방법 3

우리가 필요한것은 해당 스토리를 reactquery 와 react router 를 사용할수있게 감싸 줄수있는 컴포넌트가 필요하다 그럼 그걸 감싸주는 역할을 하는 컴포넌트를 만들어주면 된다.

storybookProvider.tsx

import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter } from "react-router";

const queryClient = new QueryClient();

const StorybookProviders = ({ children }: { children: React.ReactNode }) => (
  <QueryClientProvider client={queryClient}>
    <BrowserRouter>{children}</BrowserRouter>
  </QueryClientProvider>
);

export default StorybookProviders

preview.ts

import type { Preview } from '@storybook/react';
import StorybookProvider from '../src/utils/StorybookProvider'
import React from 'react';
import '../src/styles/global.css'


const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
  decorators: [
    (Story) => (
      <StorybookProvider>
        <Story />
      </StorybookProvider>
    ),
  ],
};

export default preview;

이렇게 해주면 제대로 작동이 되었다, preview에서 바로 사용했을때 작동을 안하는 이유는 다시 찾아보고 싶어서 나중에 추가적으로 수정을 할 예정이지만, preview tsx 가 아닌 ts 포멧을 유지하고 따로 분리해서 컴포넌트를 사용하는 방법 3도 괜찮은 방법이라고 생각합니다.

  • 더 좋은 방법이 있으면 댓글에 남겨주시면 적극적으로 검토후 반영해보겠습니다.

2개의 댓글

comment-user-thumbnail
2025년 3월 8일

충분히 고민한 과정이 잘 드러나있는 것 같아요. 다른 방법이 있는지 저도 한 번 생각해보겠습니다.

답글 달기
comment-user-thumbnail
2025년 3월 9일

으.... 오류없는 세상에서 살고 싶어요...

답글 달기