Vue3 프로젝트에 Storybook 7 설치, 적용하기

kdeun1·2023년 5월 24일
1
post-thumbnail

도입 이유

새로운 프로젝트에 투입되었으며, 컴포넌트 관점이 많이 부족한 것을 느꼈다. atomic design 기반의 컴포넌트를 개발하고 있는 와중에 컴포넌트 기획, 디자인 시스템과의 협업을 위해 스토리북 기반의 CDD 프로세스를 만들기 위해 스토리북을 도입하였다.

서비스의 품질을 높이기 위해 컴포넌트의 유지보수성과 재사용성의 퀄리티가 높아야한다고 생각하며, 스토리북을 통해 어느정도 이 부분이 해소될 것이라고 생각한다.

스토리북이 약 한달 전에 7버전이 새로 나왔으며, 6버전 적용과 약간 다른 점이 있어 이 부분도 함께 정리하기 위해 이 글을 작성하였다.

설치

  1. Vue 3 프로젝트 내 storybook을 설치해주도록 하자.
    현재 storybook v7이 lastest이다.
npx sb init
  1. 선택
Do you want to run the 'eslintPlugin' migration on your project? (Y/n)
  • eslint를 사용하여 코드를 검사하기 때문에 yes 선택
Do you want to run the 'npm7' migration on your project? (Y/n)
  • 현재 npm8을 사용하고 있다. yes 선택
  1. storybook 실행 (npm run storybook)
"scripts": {
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook",
}

위 스크립트는 정상적으로 작동하지 않는다.

위 과정처럼 storybook을 설치하면 7버전의 패키지들이 설치된다. start-storybook 명령어는 storybook 6.5.x 버전의 커멘드이기 때문에 변경이 필요하다.

storybook 7 변경점 (migration 6.5.x to 7)

변경점 중 몇 가지를 적어보았다.

  • node 버전 15이하 지원중단

  • 모던 브라우저 지원

    • 크롬 100버전 이상
    • 노드 16버전 이상
    • IE11 지원 중단
  • 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"
  }
}

이어서 설치

  1. node 버전이 16이상인지 확인한다.

  2. 브라우저가 IE11이 아니고 크롬 100버전 이상인지 확인한다.

  3. 스토리북 7.0 버전부터 addon-docs에 필요한 react, react-dom이 필요하다. 이를 설치해주자.

    npm add react react-dom --dev
  1. package.json 파일의 scripts에 storybook 명령어를 수정한다.
// AS-IS
{
  "scripts": {
    "storybook": "start-storybook <some flags>",
    "build-storybook": "build-storybook <some flags>"
  }
}
  • start-storybook, build-storybook 명령어를 아래와 같이 변경한다.
// TO-BE
{
  "scripts": {
    "storybook": "storybook dev <some flags>",
    "build-storybook": "storybook build <some flags>"
  },
  "devDependencies": {
  	// ...
    "storybook": "^7.0.18"
  }
}
  1. 유효한 프레임워크 패키지를 설치해준다. 현재 프로젝트의 환경은 webpack5, vue이므로 New Framework API 항목에서 @storybook/vue3-webpack5를 설치한다.
npm i -D @storybook/vue3-webpack5
  1. 설치된 패키지에 맞게 .storybook 폴더의 main.js 파일 안에 옵션을 변경한다. 이는 Framework field mandatory를 보고 참고한다.
// 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"
  }
}
  • "framework" 필드의 값을 아래와 같이 변경한다.
// 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 하여 만들었다.

args

최초 전달인자를 정의한다.

argTypes

전달인자의 타입과 옵션, 설명을 정의한다. annotation의 data type은 boolean, number, object 등의 존재하며, select, radio, check 등의 control을 통해 사용자의 동작을 제한할 수 있다.

추가 세팅

scss

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 모듈을 읽는 방식이 내부에 들어있다.

alias

컴포넌트 파일에 import path에 '@'와 같은 alias를 사용하는 경우가 있다. .storybook 폴더에 main.js 파일에 다음과 같이 내용을 추가해준다.

// .storybook/main.js
  webpackFinal: async (config) => {
	//...
    config.resolve.alias = {
      ...config.resolve.alias,
      '@': path.resolve(__dirname, '..', 'src'),
    };

    return config;
  }

SassError

컴포넌트 스타일 구현을 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같은 플러그인들처럼 효율적으로 스토리를 작성할 수 있을 것 같다.

서비스 내 대부분의 컴포넌트에 스토리북을 적용하고 모노레포도 함께 적용해보고 싶다.

profile
프론트엔드 개발자입니다.

0개의 댓글