Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ dist-ssr
*.sln
*.sw?

.env
.env
*storybook.log
storybook-static
51 changes: 51 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { StorybookConfig } from '@storybook/react-vite';
import { mergeConfig } from 'vite';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],

addons: [
'@storybook/addon-docs',
'@storybook/addon-a11y',
],

framework: '@storybook/react-vite',

viteFinal: async (storybookConfig) =>
mergeConfig(storybookConfig, {
resolve: {
alias: {
'@': resolve(__dirname, '../src'),
'@app': resolve(__dirname, '../src/app'),
'@pages': resolve(__dirname, '../src/pages'),
'@widgets': resolve(__dirname, '../src/widgets'),
'@features': resolve(__dirname, '../src/features'),
'@shared': resolve(__dirname, '../src/shared'),
'@images': resolve(__dirname, '../src/shared/assets/images'),
'@icons': resolve(__dirname, '../src/shared/assets/icons'),
'@api': resolve(__dirname, '../src/shared/api'),
'@config': resolve(__dirname, '../src/shared/config'),
'@model': resolve(__dirname, '../src/shared/model'),
'@styles': resolve(__dirname, '../src/shared/styles'),
'@types': resolve(__dirname, '../src/shared/types'),
'@ui': resolve(__dirname, '../src/shared/ui'),
'@lib': resolve(__dirname, '../src/shared/lib'),
},
},
}),
Comment on lines 12 to 15
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스토리북 파일 내부에서도 path alias형태로 가져올 수 있게 정의해준 점 너무 좋은 것 같아요!
vite.config.ts, tsconfig.app.json에서 새로운 path가 추가될 때마다 스토리북 내부에서도 이를 같이 동기화시킬 필요가 있을 것 같아서 vite-tsconfig-paths 플러그인으로 싱크 맞춰주는 방법도 한 번 제안드려봐요 🎵

Suggested change
viteFinal: async (storybookConfig) =>
mergeConfig(storybookConfig, {
resolve: {
alias: {
'@': resolve(__dirname, '../src'),
'@app': resolve(__dirname, '../src/app'),
'@pages': resolve(__dirname, '../src/pages'),
'@widgets': resolve(__dirname, '../src/widgets'),
'@features': resolve(__dirname, '../src/features'),
'@shared': resolve(__dirname, '../src/shared'),
'@images': resolve(__dirname, '../src/shared/assets/images'),
'@icons': resolve(__dirname, '../src/shared/assets/icons'),
'@api': resolve(__dirname, '../src/shared/api'),
'@config': resolve(__dirname, '../src/shared/config'),
'@model': resolve(__dirname, '../src/shared/model'),
'@styles': resolve(__dirname, '../src/shared/styles'),
'@types': resolve(__dirname, '../src/shared/types'),
'@ui': resolve(__dirname, '../src/shared/ui'),
'@lib': resolve(__dirname, '../src/shared/lib'),
},
},
}),
import tsconfigPaths from 'vite-tsconfig-paths'; // 별도 설치
export default {
viteFinal: async (config) => {
/* 생략 */
return mergeConfig(config, {
plugins: [tsconfigPaths()],
});
},
};


typescript: {
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => !/node_modules/.test(prop.parent?.fileName || ''),
},
Comment on lines 19 to 23
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옵셔널 타입의 경우(ex. disabled?: boolean), storybook상에 생성될 문서에 boolean(true, false) 뿐만 아니라 undefined까지 함께 보여진다고 해요.
현재 Button예제는 react-docgen-typescript이 기본적으로 undefined를 제거하는 방향으로 동작하고 있지만, 더 복잡한 타입의 경우에는 undefined가 그대로 노출되는 경우도 있다고 해서, undefined는 제거하여 docs를 만들어주는 옵션인 shouldRemoveUndefinedFromOptional : true도 함께 정의해주면 좀 더 보기 좋은 storybook docs가 만들어질 것 같아요🥰💞

},
};

export default config;
22 changes: 22 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Preview } from '@storybook/react-vite';

// 나중에 전역 스타일 여기서 import
// vanilla-extract의 globalStyle / theme entry 여기서 불러옴
// 예: import '@/shared/styles/global.css.ts';

const preview: Preview = {
parameters: {
layout: 'padded',

actions: { argTypesRegex: '^on[A-Z].*' },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가적으로 합세 스토리북 세팅할 때 발견했던 부분인데,
{ argTypesRegex: '^on[A-Z].*' }옵션은 on으로 시작하고 그 다음에는 대문자로 시작하는 이벤트 핸들러 props(ex. onClick, onChange)에 대해 자동으로 actions으로 연결해주는 방식이에요.

하지만 v8버전 이상부터는 { argTypesRegex: '^on[A-Z].*' } 옵션을 권장하고 있지 않다고 합니다! (실제로 합세 때 해당 옵션을 사용했을 때 경고 콘솔이 뜨더라구요)
https://storybook.js.org/docs/essentials/actions#automatically-matching-args
대신 @storybook/test에서 제공하는 fn()을 사용하여 스토리북 단에서 명시적으로 이벤트 핸들러를 정의하는 방식을 권장하고 있다고 해요!

args: {
  onClick: fn(), 
}

현재는 별도 경고는 안 떠서 괜찮아 보이긴 합니다!

다만 공식 문서에서 권장하는 fn()은 주로 인터랙션 테스트(@storybook/test) 를 위한 기능이라, 저희가 당장 테스트 자동화를 도입할 게 아니라면 굳이 라이브러리를 추가로 설치해서 무겁게 갈 필요는 없을 것 같습니다.
작성해주신 Button 스토리처럼 argTypes에 action: 'clicked'를 명시하면 로그 확인이 충분히 가능하니, 우선은 추가 설치 없이 preview.tsargTypesRegex 설정만 제거하는 방향으로 가볍게 유지해도 좋을 것 같아요!👍🏻

// 나중에 입력값 등을 상세히 보고 싶을 땐 action 함수를 사용하는 방식도 있다고 하네요!
import { action } from '@storybook/addon-actions';

const meta = {
  // ...
  args: {
    onClick: action('clicked'),
    onChange: (e) => action('onChange')(e.target.value),
  },
};

저도 스토리북을 제대로 활용해본 경험은 아직 없어서😭 같이 배워가면서 한 번 열심히 스토리북 채워봅시다!!


controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
Comment on lines +13 to +18
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스토리북의 대부분 컴포넌트에서 docs 파일이 생성될 거라면, preview.ts파일에 tags: ["autodocs"] 옵션을 미리 추가해주는 것도 좋을 것 같아요🙌🏻

Suggested change
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
tags: ["autodocs"],

},
};

export default preview;
3 changes: 3 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
import storybook from "eslint-plugin-storybook";

import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,29 @@
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@storybook/addon-a11y": "^10.1.11",
"@storybook/addon-docs": "^10.1.11",
"@storybook/react-vite": "^10.1.11",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react-swc": "^4.2.2",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"eslint-plugin-storybook": "^10.1.11",
"globals": "^16.5.0",
"storybook": "^10.1.11",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.4",
"vite": "^7.2.4"
Expand Down
Loading