새로운 프로젝트에 투입되었으며, 컴포넌트 관점이 많이 부족한 것을 느꼈다. atomic design 기반의 컴포넌트를 개발하고 있는 와중에 컴포넌트 기획, 디자인 시스템과의 협업을 위해 스토리북 기반의 CDD 프로세스를 만들기 위해 스토리북을 도입하였다.
서비스의 품질을 높이기 위해 컴포넌트의 유지보수성과 재사용성의 퀄리티가 높아야한다고 생각하며, 스토리북을 통해 어느정도 이 부분이 해소될 것이라고 생각한다.
스토리북이 약 한달 전에 7버전이 새로 나왔으며, 6버전 적용과 약간 다른 점이 있어 이 부분도 함께 정리하기 위해 이 글을 작성하였다.
npx sb init
Do you want to run the 'eslintPlugin' migration on your project? (Y/n)
Do you want to run the 'npm7' migration on your project? (Y/n)
"scripts": {
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
}
위 스크립트는 정상적으로 작동하지 않는다.
위 과정처럼 storybook을 설치하면 7버전의 패키지들이 설치된다. start-storybook 명령어는 storybook 6.5.x 버전의 커멘드이기 때문에 변경이 필요하다.
변경점 중 몇 가지를 적어보았다.
node 버전 15이하 지원중단
모던 브라우저 지원
React peer dependency 관련 : 7.0버전부터는 addon-docs
를 사용할 때 react
, react-dom
패키지가 필요하다.
start-storybook / build-storybook 바이너리 삭제. 명령어를 아래와 같이 변경한다.
{
"scripts": {
"storybook": "storybook dev <some flags>",
"build-storybook": "storybook build <some flags>"
},
"devDependencies": {
"storybook": "next"
}
}
스토리북 7.0부터 webpack4는 지원 중단
새로운 프레임워크 API : frameworks(e.g. React, Vue), builders(e.g. Webpack, Vite)에 대한 구성을 추상화하여 통합을 쉽게 하는 프레임워크 개념을 도입
참고 : https://github.com/storybookjs/storybook/blob/next/MIGRATION.md
node 버전이 16이상인지 확인한다.
브라우저가 IE11이 아니고 크롬 100버전 이상인지 확인한다.
스토리북 7.0 버전부터 addon-docs에 필요한 react, react-dom이 필요하다. 이를 설치해주자.
npm add react react-dom --dev
// AS-IS
{
"scripts": {
"storybook": "start-storybook <some flags>",
"build-storybook": "build-storybook <some flags>"
}
}
// TO-BE
{
"scripts": {
"storybook": "storybook dev <some flags>",
"build-storybook": "storybook build <some flags>"
},
"devDependencies": {
// ...
"storybook": "^7.0.18"
}
}
@storybook/vue3-webpack5
를 설치한다.npm i -D @storybook/vue3-webpack5
// AS-IS
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions"
],
"framework": "@storybook/vue3",
"core": {
"builder": "@storybook/builder-webpack5"
}
}
// TO-BE
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions"
],
"framework": {
"name": '@storybook/vue3-webpack5',
"options": {},
},
"core": {
"builder": "@storybook/builder-webpack5"
}
}
import type { Meta, StoryObj } from '@storybook/vue3';
import SquareBox from './SquareBox.vue';
const meta: Meta<typeof SquareBox> = {
title: 'Atom/SquareBox',
component: SquareBox,
tags: ['autodocs'],
argTypes: {
size: {
control: { type: 'select' },
options: ['small', 'medium', 'large'],
description: '',
},
color: {
control: { type: 'select' },
options: ['default', 'red', 'blue', 'yellow', 'dark'],
description: '',
},
hover: {
description: '',
},
border: {
control: { type: 'object' },
description: '',
},
},
};
export default meta;
type Story = StoryObj<typeof SquareBox>;
export const Default: Story = {
render: (args) => ({
components: { SquareBox },
setup() {
return { args };
},
template: '<square-box v-bind="args" />',
}),
args: {
size: 'small',
color: 'default',
hover: false,
border: { style: 'none', width: 1 },
},
};
위 코드는 Storybook 공식문서 가이드를 참고하여 작성한 스토리다. Args 메뉴에서 Vue 3버전 샘플을 통해 args를 composition 하여 만들었다.
최초 전달인자를 정의한다.
전달인자의 타입과 옵션, 설명을 정의한다. annotation의 data type은 boolean, number, object 등의 존재하며, select, radio, check 등의 control을 통해 사용자의 동작을 제한할 수 있다.
storybook에서 scss 모듈을 읽지 못하는 문제가 존재한다. 이때는 .storybook 폴더에 main.js에 추가적인 세팅이 필요하다.
// main.js
const path = require('path');
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions"
],
"framework": {
"name": '@storybook/vue3-webpack5',
"options": {},
},
"core": {
"builder": "@storybook/builder-webpack5"
},
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
});
return config;
}
}
webpack에서 scss loader를 통해 scss 모듈을 읽는 방식이 내부에 들어있다.
컴포넌트 파일에 import path에 '@'와 같은 alias를 사용하는 경우가 있다. .storybook 폴더에 main.js 파일에 다음과 같이 내용을 추가해준다.
// .storybook/main.js
webpackFinal: async (config) => {
//...
config.resolve.alias = {
...config.resolve.alias,
'@': path.resolve(__dirname, '..', 'src'),
};
return config;
}
컴포넌트 스타일 구현을 scss로 사용하고 있다. @mixin 함수를 읽지 못하는 현상이 존재한다. webpack의 additionalData 옵션을 storybook에도 적용해준다.
// .storybook/main.js
webpackFinal: async (config) => {
//...
config.module.rules.push({
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
additionalData: '@import "@/stylesheets/utils/index.scss";'
}
}
],
include: path.resolve(__dirname, '../'),
});
return config;
}
컴포넌트 scss 내 themify나 themed와 같은 @mixin을 정상적으로 호출할 수 있게 된다.
설치가 완료되었지만 실제 컴포넌트에 스토리북을 반영해보고 컴포넌트 디자인 문서화도 하나씩 추가할 예정이다.
아마 하나씩 추가하면서 생기는 문제들은 이 글에 꾸준히 추가할 예정이다.
나중에 시간이 된다면 스토리의 ArgsTypes를 자동으로 추론하여 타입 코드를 생성해주는 vue-docgen-api를 사용해보고 싶다. codegen이나 openapi generator같은 플러그인들처럼 효율적으로 스토리를 작성할 수 있을 것 같다.
서비스 내 대부분의 컴포넌트에 스토리북을 적용하고 모노레포도 함께 적용해보고 싶다.