더 알아보기/기능

[StoryBook] Vue 3 + Typescript 프로젝트에 StoryBook 적용하기

은돌1113 2024. 9. 27. 15:23

Vue 3와 TypeScript를 사용한 프로젝트에서 폴더 구조를 고민하던 중, 컴포넌트를 테스트하고 문서화할 수 있는 방법으로 Storybook을 도입하기로 결정했다.

 

StoryBook에 대해 알아보니, Vue에서 적용하는 방법에 대한 자료가 React보다 적기도 하고, 현재 진행 중인 프로젝트는 서브모듈을 사용하고 있어서 이번 기회에 정리해 두면 좋을 것 같았다.

 

사용 후기부터 얘기하자면, 처음에 적용할 때는 좀 낯설기도 하고 자동으로 문서화가 된다고 해도 설정을 해줘야 하니까 시간이 꽤 걸릴 것 같아서 비효율적이지 않을까? 하는 의문이 들었는데, 막상 해보니 예상보다 쉽게 설정할 수 있었고, 문서화가 잘 돼서 일일이 코드를 파악하지 않아도 된다는 점이 정말 좋았다.

 

설치 및 설정 방법

※ node version 18 이상부터 storyBook을 사용할 수 있다.

 

1️⃣Vue3 + Typescript 프로젝트 생성

npm init vue@latest

 

글쓴이는 위와 같이 설정했지만, 상황에 따라 속성은 다르게 설정해주면 된다.


2️⃣ StoryBook 설치

npx storybook@latest init

위 명령어를 터미널에 입력하면 사진처럼 설치가 된다.

설치가 끝나면 root에 .storybook, src에 stories 폴더가 생성된다.


3️⃣ StoryBook 실행 방법

npm run storybook

기존 화면


Sass를 사용하는 경우

#.stories.ts 파일에서 Sass가 포함된 컴포넌트를 작성하면 sass-embedded 오류가 발생할 수 있습니다. 이는 Storybook이 Sass 파일을 컴파일하기 위해 sass-embedded 패키지가 필요하기 때문입니다. 이 오류는 주로 Sass 코드가 Storybook 환경에서 제대로 처리되지 않을 때 발생하기 때문에, 아래 명령어를 사용해 sass-embedded 패키지를 설치하면 됩니다.

npm install -D sass-embedded

 

#.stories.ts 작성 방법

storyBook 설치 시 자동으로 생성되는 #.stories.ts 외에도 아래와 같은 형식으로 작성할 수도 있습니다.

 

  • /component/checkBtnForSelect.vue
<template>
    <div :class="`id-radioCheckBtn cursor-p ${props.isCheck ? 'radioCheckBtn__checked' : ''}`">
        <div class="radioCheckBtn__innerCircle"></div>
    </div>
</template>

<script setup lang="ts">
const props = defineProps({
    isCheck: { type: Boolean, default: false },
    topPosition: { type: String, require: true, default: '0' },
    leftPosition: { type: String, require: true, default: '0' },
});
</script>

<style scoped lang="scss">
.id-radioCheckBtn {
    width: 15px;
    height: 15px;
    background-color: #fff;
    position: absolute;
    top: v-bind(topPosition);
    left: v-bind(leftPosition);
    border-radius: 50%;
    border: 1px solid #a3a6ad;

    &::before {
        content: '';
        position: absolute;
        top: -7px;
        left: -8px;
        width: 30px;
        height: 30px;
    }
}

.radioCheckBtn__innerCircle {
    display: none;
    position: absolute;
    top: calc(50% - 4.5px);
    right: calc(50% - 4.5px);
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background-color: #00adff;
}

.radioCheckBtn__checked {
    border: 1px solid #00adff;
    .radioCheckBtn__innerCircle {
        display: block;
    }
}
</style>
  • /stories/CheckBtnForSelect.stories.ts
import CheckBtnForSelect from '../components/checkBtnForSelect.vue';

export default {
    title: 'Units/CheckBtnForSelect',
    component: CheckBtnForSelect,
    tags: ['autodocs'],
    // argTypes : 인수의 타입과 설명을 정의하는 객체
    argTypes: {
        isCheck: { control: 'boolean', description: '체크 상태' },
        topPosition: { control: 'text', description: 'top 설정값' },
        leftPosition: { control: 'text', description: 'left 설정값' },
    },
    // args : 기본 속성 값들을 정의하는 객체
    args: {
        isCheck: false,
        topPosition: '0',
        leftPosition: '0',
    },
};

const Template = (args: { isCheck: boolean; topPosition: string; leftPosition: string }) => ({
    components: { CheckBtnForSelect },
    setup() {
        return { args };
    },
    template: '<CheckBtnForSelect v-bind="args" />',
});

export const Checked: any = Template.bind({});
Checked.args = {
    isCheck: true,
    topPosition: '0',
    leftPosition: '0',
};

export const Unchecked: any = Template.bind({});
Unchecked.args = {
    isCheck: false,
    topPosition: '0',
    leftPosition: '0',
};