๐Ÿ†•

getStart

ย 
์•„๋ฌด๋ž˜๋„ Nextjs, tailwind, shadCN๋“ฑ ์—ฌ๋Ÿฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ˜ผ์žฌ๋œ ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉํ•˜๋ ค๋‹ˆ ์—ฌ๋Ÿฌ ์—๋กœ์‚ฌํ•ญ๋“ค์ด ์žˆ์—ˆ๋‹ค.
ย 

1. install(nextjs)

npx storybook@latest init

nextjs Storybook tailwind ์„ค์น˜ํ•  ๋•Œ ์ถ”๊ฐ€์ ์œผ๋กœ ์„ค์น˜ํ•˜๊ธฐ!

npm install -D postcss autofrefixer
/** @type {import('postcss-load-config').Config} */ export const plugins = { tailwindcss: {}, autoprefixer: {}, };

์˜์กด์„ฑ ๋ชฉ๋ก

"devDependencies": { "@chromatic-com/storybook": "^1.3.5", "@storybook/addon-a11y": "^8.0.10", "@storybook/addon-essentials": "^8.0.10", "@storybook/addon-interactions": "^8.0.10", "@storybook/addon-links": "^8.0.10", "@storybook/addon-onboarding": "^8.0.10", "@storybook/addon-styling-webpack": "^1.0.0", "@storybook/blocks": "^8.0.10", "@storybook/nextjs": "^8.0.10", "@storybook/react": "^8.0.10", "@storybook/test": "^8.0.10", "autoprefixer": "^10.4.19", "postcss": "^8.4.38", "prettier-plugin-tailwindcss": "^0.5.14", "sass": "^1.77.1", "storybook": "^8.0.10", "tailwindcss": "^3.4.3", "@svgr/webpack": "^8.1.0", // svg ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์˜์กด์„ฑ }
ย 
ย 
์ดํ›„ preview.ts ์— tailwind๊ฐ€ ์žˆ๋Š” css ํด๋” import ํ•ด์ฃผ๊ธฐ
ย 
์ดํ›„ ๋นŒ๋“œ์‹œ ์Šคํ† ๋ฆฌ ํŒŒ์ผ๋“ค์€ ์ œ์™ธํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์—
next.config์—์„œ ๋นŒ๋“œ์‹œ ์ œ์™ธ๋ชฉ๋ก์— ์ถ”๊ฐ€
ย 
ย 
// next.config.mjs /** @type {import('next').NextConfig} */ const nextConfig = { // ์Šคํ† ๋ฆฌ๋ถ ํ™•์žฅ์ž ์ œ๊ฑฐ webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => { config.module.rules.push({ test: /\.stories\.(ts|js|tsx|jsx)$/, loader: 'ignore-loader' }); // ์Šคํ† ๋ฆฌ๋ถ ๋ฃจํŠธ๊ฒฝ๋กœ์ œ๊ฑฐ config.module.rules.push({ test: /\.storybook/, loader: 'ignore-loader' }); return config; }, }; export default nextConfig;
ย 
์•„๋ž˜๋Š” alias์™€ nextjs ํ™˜๊ฒฝ์—์„œ SVG๋ฅผ ์ ์šฉํ•˜๋Š” ์›นํŒฉ์„ค์ • SVG๋Š” nextConifg์—์„œ๋„ ์ ์šฉํ•ด์ค˜์•ผํ•จ ์•„๋ž˜ ์ฐธ๊ณ 
Nextjs ์—์„œ SVG ํ™œ์šฉํ•˜๊ธฐ
// .storybook/main.js import type { StorybookConfig } from '@storybook/nextjs'; import path from 'path'; const config: StorybookConfig = { 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', // ์ปดํฌ๋„ŒํŠธ ์ƒํ˜ธ์ž‘์šฉ ํ…Œ์ŠคํŠธ '@storybook/addon-a11y', // ์›น ์ ‘๊ทผ์„ฑ ๊ฒ€์‚ฌ. ], framework: { name: '@storybook/nextjs', options: {}, }, docs: { autodocs: 'tag', }, staticDirs: ['..\\public'], webpackFinal: async config => { config.module = config.module || {}; config.module.rules = config.module.rules || []; // This modifies the existing image rule to exclude .svg files // since you want to handle those files with @svgr/webpack // svg ์ธ์‹ํ•˜๊ธฐ ์œ„ํ•œ ์›นํŒฉ ์„ค์ • const imageRule = config.module.rules.find(rule => rule?.['test']?.test('.svg')); if (imageRule) { imageRule['exclude'] = /\.svg$/; } // Configure .svg files to be loaded with @svgr/webpack // svg ์ธ์‹ํ•˜๊ธฐ ์œ„ํ•œ ์›นํŒฉ ์„ค์ • config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], }); // @ alias ์„ค์ • if (config.resolve) { config.resolve.alias = { ...config.resolve.alias, '@': path.resolve(__dirname, '../src'), }; } return config; }, }; export default config;
ย 
ย 
lint์—์„œ๋„ ์ปดํŒŒ์ผ์‹œ ์ œ์™ธํ•˜๋„๋ก ์„ค์ •ํ•œ๋‹ค. tsconfig๋„ ์ œ์™ธํ•˜๋ คํ–ˆ๋Š”๋ฐ
์ด๋Ÿด๊ฒฝ์šฐ ์Šคํ† ๋ฆฌ๋ถ ํŒŒ์ผ์—์„œ ํƒ€์ž…์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ด์•ผํ•  ๊ฒƒ ๊ฐ™์Œ.
{ "extends": [ "next/core-web-vitals", "prettier", "plugin:storybook/recommended" ], "ignorePatterns": [ "**/*.stories.tsx", "**/*.stories.ts", "**/*.stories.js", "**/*.stories.jsx", ".storybook/**" ] }
ย 
ย 

2. ์‹คํ–‰

npm run storybook
ย 
ย 
ย 
ย 
ย