스토리북이란?
최근 스토리북(Storybook)에 대해 알게되었다.
스토리북이란 FE 개발에서 필수적인 컴포넌트를 문서화하고 시각화할 수 있는 도구이다.
props와 onClick 등 다양한 매개변수에 대한 테스트도 가능하며, 페이지 자체도 구동할 수 있다.
또 문서화된 스토리북 자체를 배포해서 협업에 유용하게 사용할 수 있다!
물론 정석적으로는 스토리북과 함께 개발하는 것이 베스트겠지만
공부를 위해 현재 진행하고 있는 프로젝트에 스토리북을 임시로 설치해봤다.
이번 연습을 통해 스토리북을 익혀서 다음 프로젝트에 적용할 수 있었으면 좋겠다.
설치 방법
npx storybook@latest init
실행방법
npm run storybook
실행 후 localhost 6006번에 접속하면 된다.
다음과 같은 화면이 뜬다면 성공!
기본 설정하기
이제 내가 만든 컴포넌트에 적용해보도록 하자.
우리 프로젝트의 버튼을 스토리북에 추가하는 방법을 익혀보도록 하겠다.
src에 새롭게 생긴 파일들을 전부 지우고, Button.stories.js만 수정할 것이다.
먼저 프로젝트를 React+Vite로 빌드했기 때문에 관련 설정이 필요하다.
babel 및 esmodule에 대한 추가적인 설정을 위해 vite 설정 파일을 다음과 같이 설정한다.
해당 설정은 프로젝트의 스택과 버전 등 사람마다 매우 다르므로.. 알아서 잘 해야 함. ㅎㅎ
프로젝트 루트 초기 세팅
./vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";
import babel from "@rollup/plugin-babel";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
svgr(),
babel({
babelHelpers: "bundled",
presets: ["@babel/preset-react"],
exclude: "node_modules/**"
})
],
css: {
modules: {
// CSS Module 추가 설정
generateScopedName: "[name]__[local]___[hash:base64:5]"
}
},
....
esbuild: {
loader: "jsx" // JSX 파일을 처리할 로더로 설정
}
});
./babel.config.js
module.exports = {
presets: [
"@babel/preset-react" // React JSX 처리
]
};
.storybook 폴더 내 파일 수정
.strory/main.js
/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {
stories: ["../src/**/*.mdx", "../src/**/*.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/react-vite",
options: {}
}
};
export default config;
React+Vite에서 구동했다보니 알아서 이렇게 자동으로 설정된 모양.
애드온을 설치한 경우 여기서 설정하면 된다고 하는데 일단 pass~
.storybook/preview.js
이 부분에서 좀 애를 많이 먹었는데..
일단 프리뷰 파일의 경우 모든 스토리들에 공통적으로 적용해야하는 설정을 해야한다.
일단 버튼 컴포넌트만 테스트해볼거라 다음과 같은 설정을 추가로 진행 했다.
- var 컬러 변수 및 버튼 모듈 css를 위한 index.css 추가
- themeSiteAtom(Jotai state)에 의해서 제어되는 themeProvider를 설정
import React, { useEffect } from "react";
import { ThemeProvider } from "styled-components";
import { CustomThemeProvider } from "../src/styles/CustomThemeProvider";
import { MemoryRouter } from "react-router-dom";
import theme from "../src/styles/theme";
import "../src/index.css";
import "../src/components/button/Button.module.css";
import { useAtom } from "jotai";
import { themeSiteAtom } from "../src/store/atom";
const preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i
}
}
},
decorators: [
(Story) => {
const [themeSite, setThemeSite] = useAtom(themeSiteAtom);
useEffect(() => {
const sessionTheme =
sessionStorage.getItem("theme")?.replace("-theme", "") || "default";
setThemeSite(sessionTheme);
}, [setThemeSite]);
return (
<CustomThemeProvider>
<ThemeProvider theme={theme}>
<MemoryRouter>
<div className={`${themeSite}`}>
<Story />
</div>
</MemoryRouter>
</ThemeProvider>
</CustomThemeProvider>
);
}
]
};
export default preview;
스토리 파일 만들기
자! 이제 모든 초기 세팅이 끝났다. (사실 이게 메인으로 힘들었음)
이제 ./stories 폴더로 가서 원하는 스토리를 만들면 된다.
작성에 활용한 버튼 컴포넌트 파일과 css는 이 링크 에서 확인 가능하다:)
easy-ticket/src/components/button/Button.jsx at main · Easy-Ti-cket/easy-ticket
누구나 쉽게 배우는 티켓팅 플랫폼, 쉽게🎫티켓! Contribute to Easy-Ti-cket/easy-ticket development by creating an account on GitHub.
github.com
./stories/Button.stories.jsx
타이틀, 컴포넌트명을 입력하고 argTypes안에 담을 수 있는 options을 배열로 작성한다.
프로젝트 내에서 CSS Module에서 type을 props로 받아, css를 다르게 설정해두었고 theme 정보에 따라 세션스토리지 값이 바뀌고 이에 따라 버튼의 테마클래스가 결정되기 때문에 type과 theme이라는 정보를 추가해두었다.
이렇게 설정하면 스토리북 구동 시 옵션을 선택해서 손 쉽게 버튼 타입을 바꿀 수 있다.
import React, { useEffect } from "react";
import Button from "../components/button/Button";
import { useAtom } from "jotai";
import { themeSiteAtom } from "../store/atom";
export default {
title: "Components/Button",
component: Button,
argTypes: {
type: {
options: [null, "outline", "help", "mode", "modal", "record", "outro"],
control: { type: "select" },
defaultValue: null
},
theme: {
options: ["interpark", "melonticket", "ticketlink", "yes24"],
control: { type: "select" },
defaultValue: null
},
onClick: { action: "clicked" }
}
};
const Template = (args) => {
const [themeSite, setThemeSite] = useAtom(themeSiteAtom);
useEffect(() => {
if (args.theme) {
setThemeSite(args.theme);
}
}, [args.theme, setThemeSite]);
return <Button {...args} />;
};
이제 작성한 템플릿을 args를 활용해 export로 내보내보자.
설정한 타입을 이리저리 바꾸어보면서 bind를 통해 묶어주고 다시 내보내면 끝!
내가 작성한 여러가지 버튼 템플릿들이다.
export const DefaultButton = Template.bind({});
DefaultButton.args = {
text: "디폴트 버튼",
type: null,
onClick: () => {}
};
export const OutlineButton = Template.bind({});
OutlineButton.args = {
text: "아웃라인 버튼",
type: "outline",
onClick: () => {}
};
export const HelpButton = Template.bind({});
HelpButton.args = {
text: "도움말 버튼",
type: "help",
onClick: () => {}
};
export const ModeButton = Template.bind({});
ModeButton.args = {
text: "모드 선택",
type: "mode",
onClick: () => {},
icon: "../../public/assets/images/icons/site/interpark.svg" // 아이콘 추가
};
export const ModalButton = Template.bind({});
ModalButton.args = {
text: "모달 버튼",
type: "modal",
onClick: () => {}
};
export const RecordButton = Template.bind({});
RecordButton.args = {
text: "기록보기 버튼",
type: "record",
onClick: () => {}
};
export const OutroButton = Template.bind({});
OutroButton.args = {
text: "아웃트로 버튼",
type: "outro",
onClick: () => {}
};
스토리북에서 컴포넌트 확인하기
쨘! 내가 만들어둔 버튼들이 이렇게 잘 나오는 것을 확인할 수 있다.
코드를 수정하면 바로바로 반영되어서 확인 가능하니 너무 좋다..
그리고 type과 theme를 설정해두었기 때문에, 선택하면 바뀐다!
아웃라인 타입, 멜론 테마를 각각 적용한 모습이다.
저렇게 값을 select하면서 바꿔보니 확실히 컴포넌트의 재사용이 얼마나 중요한지 실감하게 된다..
이렇게 export해둔 버튼들도 눌러보면서 잘 설정된 것을 확인했다.
페이지 설정이나 배포와 같은 추가적인 기능은 다음에 제대로 써보기로 했다.
배포는 공식 문서에 설명이 잘 되어있고, 생각보다 간단해서 꼭 도전해볼 예정이다🙂
스토리북 사용 후 느낀 점
컴포넌트를 지금껏 많이 만들어보았지만, 테스트할때마다 빈 페이지에서 하는게 불편했다.
또 협업 시 다른 팀원들과 컴포넌트를 나눠서 만들 때가 많았는데, 내가 작성하지 않은 부분에 대해서 꼼꼼히 읽어보고 또 다시 테스트해보고.. 이런 과정이 시간이 오래걸렸다.
스토리북이 초기 설정은 좀 귀찮아도 한 번 해두면 함께 작업하고 피드백할 때 아주 편할 것 같다.
또 이 프로젝트는 순수 자바스크립트로만 진행되어서 도입하기 좀 불편하지만
나중에 타입스크립트로 프로젝트를 진행했을때는 더 편리하지 않을까 하는 생각이다...!
🔽아래 공식문서에서 더 자세한 정보를 확인하자.
Storybook Tutorials
Learn how to develop UIs with components and design systems. Our in-depth frontend guides are created by Storybook maintainers and peer-reviewed by the open source community.
storybook.js.org