라이브러리와 함께 하는 Storybook 설정하기

Sming·2022년 10월 23일
17

다들 스토리북을 사용하면서 겪었을 문제라고 생각된다. 여러 라이브러리와 함께 이용할 시 골치아프다는것..

지금부터 외부 라이브러리들과 스토리북을 함께 이용할때 스토리북을 설정하는 법을 알아보자!

Storybook with React router v6

먼저 react router를 사용한다면 컴포넌트에 BrowserRouter를 감싸주어야 합니다.

export default {
	title: 'Components/SomeComponent',
	component: SomeComponent,
	decorators: [
		(Story) => (
			<BrowserRouter>
				<Story />
			</BrowserRouter>
		),
	],
} as Meta;

하지만 이 설정만 할시에는 useParams, useLocation에서 값을 알아듣지 못한 문제가 있습니다. 이 부분은 어떻게 해결할까요?

module.exports = {
	stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
	addons: [
		'@storybook/addon-links',
		'@storybook/addon-essentials',
		'@storybook/addon-interactions',
		'storybook-addon-react-router-v6',
	],

간단하게 storybook-addon-react-router-v6 addon을 설치후 main.js에 다음과 같이 넣어주면 됩니다.


	parameters: {
		reactRouter: {
			routePath: URL.VOTE_DEADLINE_GENERATOR,
			routeState: { articleId: '0', items: ['투표1', '투표2', '투표3'] },
		},
	},

또한 스토리 단에서 parameters에 다음과 같은 설정을 해주면 됩니다. 이때는 BrowserRouter로 감싸면 안됩니다.

  • routerPath: 현재 url을 명시합니다. 실제 useParams의 값으로 나오는 부분입니다.
  • routeState: navigate의 두번째 인자로 상태를 줄때 넣어주는 곳입니다. useLocation의 state로 받을 수 있습니다.

storybook with recoil

recoil을 이용하려면 recoilRoot가 컴포넌트에 감싸져있어야 합니다. RecoilRoot 같은 경우 모든곳에 감싸져 있어야하기에 preview.js의 decorators에 넣어줍니다.


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

이렇게 이용할 시 recoil은 문제없이 이용가능합니다. 하지만 recoil의 값에 따라 보이는 상태가 다른 컴포넌트를 스토리북에서 확인하고 싶다면 어떻게 할까요?

실제 프로젝트에서 SnackBar 컴포넌트를 이용하는데 이 컴포넌트는 recoil을 통한 isOpen 상태에 따라서 보임을 결정합니다. 하지만 default value가 false이기 때문에 스토리북에서는 빈 화면밖에 확일할 수 밖에 없습니다.

const MockSnackBar = () => {
	const setSnackBarState = useSetRecoilState(snackBarState);
	setSnackBarState({
		isOpen: true,
		message: '테스트입니다.',
	});

	return <SnackBar />;
};

const Template: Story = () => <MockSnackBar />;

export const DefaultSnackBar = Template.bind({});

이런 경우는 Snackbar.stories.tsx내부에 다음과 같이 MockSnackBar를 선언해준뒤 recoil상태만 바꾸어서 return하는 컴포넌트를 만들어서 처리합니다.

storybook with react-query

스토리북을 이용할때 가장 설정하기 어려운 부분입니다.

react-query를 이용할때 고려해야하는것이 3가지가 있기 때문입니다.

react-query 자체를 스토리북에서 이용하기 위한것은 어렵지 않지만 추가적으로 비동기 통신을 위한 msw, env 설정이 필요하기에 어려움이 있습니다.

react-query 설정하기

const queryClient = new QueryClient();

export const decorators = [
	(Story) => (
      <QueryClientProvider client={queryClient}>
        <Story />
      </QueryClientProvider>
	),
];

실제 앱에서 이용하듯이 QueryClientProvider를 감싸주면 됩니다.

env 설정하기

주로 API_URL을 숨기기 위해서 .env 파일을 이용할 것입니다. 하지만 .env 를 스토리북에서 이용하려면 따로 설정을 해주어야 합니다. (CRA 환경이 아닐경우)

.storybook 내에 webpack.config.js를 만들어서 dotenv 설정을 해줍니다.

const path = require('path');
const Dotenv = require('dotenv-webpack');

const envPath =
	process.env.NODE_ENV === 'development'
		? path.join(__dirname, '../webpack/.env.development')
		: path.join(__dirname, '../webpack/.env.production');

module.exports = ({ config }) => {
	config.plugins.push(
		new Dotenv({
			path: envPath,
		}),
	);

	return config;
};

이렇게 설정하고 process.env.API_URL 을 찍어보면 storybook에서는 undefined가 나오게 될것입니다.

찾아보니 storybook에서 .env에 설정한 값을 가져오려면 반드시 STORYBOOK_ 식으로 prefix를 붙혀야한다고 합니다.

const API_URL = process.env.STORYBOOk ? process.env.STORYBOOK_API_URL : process.env.API_URL;

실제 프로젝트에서는 API_URL을 다음과 같이 정의하였습니다.

msw 설정하기

먼저 storybook을 위한 msw를 초기화해줍니다.

npx msw init .storybook/public

그러면 .storybook내의 public폴더에 mockServiceWorker.js가 생성될 것입니다.

그런후 main.js의 staticDir에 storybook의 public을 가르키도록 합니다.

module.exports = {
	stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
	addons: [
		'@storybook/addon-links',
		'@storybook/addon-essentials',
		'@storybook/addon-interactions',
		'storybook-addon-react-router-v6',
	],
	framework: '@storybook/react',
	core: {
		builder: '@storybook/builder-webpack5',
	},
	staticDirs: ['./public'],
};

다음은 preview.js로 넘어가보겠습니다.

import { initialize, mswDecorator } from 'msw-storybook-addon';

initialize(); // msw-storybook-addon

export const decorators = [
	mswDecorator, // msw-storybook-addon
	(Story) => (
      <QueryClientProvider client={queryClient}>
        <Story />
      </QueryClientProvider>
	),
];

// storybook에서 msw를 실행시키기 위한 코드
if (typeof global.process === 'undefined') {
	const { worker } = require('../src/mock/browser');
	worker.start();
}

먼저 msw-storybook-addon 을 이용하여 msw를 스토리북에서 이용할 수 있도록 설정합니다.

밑의 if문은 스토리북에서 msw를 작동시키기 위한 코드입니다. 스토리북에서는 global.process가 undefined가 나오기 때문에 다음과 같은 분기 처리를 해주었습니다.

모바일 환경에서 Storybook 확인하기

특정 컴포넌트는 모바일 환경에서만 보여야 할 경우가 있을겁니다.

하지만 storybook의 default는 데스크탑이기때문에 처음 보는 사람이 보면 아무 컴포넌트도 보이지 않는 화면을 보게되어서 어색함을 느낄 수도 있기에 default를 모바일로 변경해줄 필요가 있습니다.

import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';


export default {
	title: 'Layout/MenuSlider',
	component: MenuSlider,
	decorators: [
		(Story) => (
			<BrowserRouter>
				<Story />
			</BrowserRouter>
		),
	],
	parameters: {
		viewport: {
			viewports: INITIAL_VIEWPORTS,
			defaultViewport: 'iphone6',
		},
	},
} as Meta;
profile
딩구르르

0개의 댓글