diff --git a/.storybook/main.ts b/.storybook/main.ts
new file mode 100644
index 00000000..86b75eb5
--- /dev/null
+++ b/.storybook/main.ts
@@ -0,0 +1,28 @@
+import type { StorybookConfig } from '@storybook/react-vite';
+
+import { join, dirname } from "path"
+
+/**
+* This function is used to resolve the absolute path of a package.
+* It is needed in projects that use Yarn PnP or are set up within a monorepo.
+*/
+function getAbsolutePath(value: string): any {
+ return dirname(require.resolve(join(value, 'package.json')))
+}
+const config: StorybookConfig = {
+ "stories": [
+ "../stories/**/*.mdx",
+ "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"
+ ],
+ "addons": [
+ getAbsolutePath('@chromatic-com/storybook'),
+ getAbsolutePath('@storybook/addon-docs'),
+ getAbsolutePath("@storybook/addon-a11y"),
+ getAbsolutePath("@storybook/addon-vitest")
+ ],
+ "framework": {
+ "name": getAbsolutePath('@storybook/react-vite'),
+ "options": {}
+ }
+};
+export default config;
\ No newline at end of file
diff --git a/.storybook/preview.ts b/.storybook/preview.ts
new file mode 100644
index 00000000..9017263a
--- /dev/null
+++ b/.storybook/preview.ts
@@ -0,0 +1,21 @@
+import type { Preview } from '@storybook/react-vite';
+
+const preview: Preview = {
+ parameters: {
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+
+ a11y: {
+ // 'todo' - show a11y violations in the test UI only
+ // 'error' - fail CI on a11y violations
+ // 'off' - skip a11y checks entirely
+ test: 'todo',
+ },
+ },
+};
+
+export default preview;
diff --git a/.storybook/vitest.setup.ts b/.storybook/vitest.setup.ts
new file mode 100644
index 00000000..44922d55
--- /dev/null
+++ b/.storybook/vitest.setup.ts
@@ -0,0 +1,7 @@
+import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
+import { setProjectAnnotations } from '@storybook/react-vite';
+import * as projectAnnotations from './preview';
+
+// This is an important step to apply the right configuration when testing your stories.
+// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
+setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
\ No newline at end of file
diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx
index c97741da..bd5f299c 100644
--- a/apps/client/src/App.tsx
+++ b/apps/client/src/App.tsx
@@ -16,7 +16,7 @@ function App() {
-
Vite + React
+ Vite + React \
setCount((count) => count + 1)}>
count is {count}
diff --git a/package.json b/package.json
index 68dcb20f..40c67987 100644
--- a/package.json
+++ b/package.json
@@ -7,16 +7,28 @@
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"check-types": "turbo run check-types",
- "test": "turbo run test"
+ "test": "turbo run test",
+ "storybook": "storybook dev -p 6006",
+ "build-storybook": "storybook build"
},
"devDependencies": {
+ "@chromatic-com/storybook": "^4.1.1",
"@pivanov/vite-plugin-svg-sprite": "^3.1.3",
+ "@storybook/addon-a11y": "^9.1.3",
+ "@storybook/addon-docs": "^9.1.3",
+ "@storybook/addon-vitest": "^9.1.3",
+ "@storybook/react-vite": "^9.1.3",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.14",
+ "storybook": "^9.1.3",
"turbo": "^2.5.6",
"typescript": "5.9.2",
- "vite": "7.1.2"
+ "vite": "7.1.2",
+ "vitest": "^3.2.4",
+ "@vitest/browser": "^3.2.4",
+ "playwright": "^1.55.0",
+ "@vitest/coverage-v8": "^3.2.4"
},
"pnpm": {
"overrides": {
diff --git a/packages/design-system/src/components/index.ts b/packages/design-system/src/components/index.ts
index 2d0199b4..69348d2c 100644
--- a/packages/design-system/src/components/index.ts
+++ b/packages/design-system/src/components/index.ts
@@ -1,5 +1,6 @@
export { default as Button } from './button/Button';
export { Switch } from './switch/Switch';
export { default as Input } from './input/Input';
+export { default as Level } from './level/Level';
export { Textarea } from './textarea/Textarea';
export { Progress } from './progress/Progress';
diff --git a/packages/design-system/src/components/level/Level.stories.tsx b/packages/design-system/src/components/level/Level.stories.tsx
new file mode 100644
index 00000000..ede2f81f
--- /dev/null
+++ b/packages/design-system/src/components/level/Level.stories.tsx
@@ -0,0 +1,33 @@
+import type { Meta, StoryObj } from '@storybook/react-vite';
+import Level from './Level';
+
+const meta: Meta = {
+ title: 'Components/Level',
+ component: Level,
+ tags: ['autodocs'],
+ parameters: {
+ layout: 'centered',
+ },
+ argTypes: {
+ level: {
+ control: { type: 'number', min: 1, max: 100 },
+ description: '현재 레벨 값',
+ },
+ },
+};
+export default meta;
+
+type Story = StoryObj;
+
+// 기본 예시
+export const Default: Story = {
+ args: {
+ level: 1,
+ },
+};
+
+export const Level10: Story = {
+ args: {
+ level: 10,
+ },
+};
diff --git a/packages/design-system/src/components/level/Level.tsx b/packages/design-system/src/components/level/Level.tsx
new file mode 100644
index 00000000..ebcfd5da
--- /dev/null
+++ b/packages/design-system/src/components/level/Level.tsx
@@ -0,0 +1,12 @@
+interface LevelProps {
+ level: number;
+}
+const Level = ({ level }: LevelProps) => {
+ return (
+
+ Lv.{level}
+
+ );
+};
+
+export default Level;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 41d5480f..fb8696b2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,18 +11,45 @@ importers:
.:
devDependencies:
+ '@chromatic-com/storybook':
+ specifier: ^4.1.1
+ version: 4.1.1(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))
'@pivanov/vite-plugin-svg-sprite':
specifier: ^3.1.3
version: 3.1.3(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))
+ '@storybook/addon-a11y':
+ specifier: ^9.1.3
+ version: 9.1.3(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))
+ '@storybook/addon-docs':
+ specifier: ^9.1.3
+ version: 9.1.3(@types/react@19.1.10)(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))
+ '@storybook/addon-vitest':
+ specifier: ^9.1.3
+ version: 9.1.3(@vitest/browser@3.2.4(playwright@1.55.0)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))(vitest@3.2.4))(@vitest/runner@3.2.4)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))(vitest@3.2.4(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))
+ '@storybook/react-vite':
+ specifier: ^9.1.3
+ version: 9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(rollup@4.46.2)(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))(typescript@5.9.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))
'@trivago/prettier-plugin-sort-imports':
specifier: ^5.2.2
version: 5.2.2(prettier@3.6.2)
+ '@vitest/browser':
+ specifier: ^3.2.4
+ version: 3.2.4(playwright@1.55.0)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))(vitest@3.2.4)
+ '@vitest/coverage-v8':
+ specifier: ^3.2.4
+ version: 3.2.4(@vitest/browser@3.2.4(playwright@1.55.0)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))(vitest@3.2.4))(vitest@3.2.4(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))
+ playwright:
+ specifier: ^1.55.0
+ version: 1.55.0
prettier:
specifier: ^3.6.2
version: 3.6.2
prettier-plugin-tailwindcss:
specifier: ^0.6.14
version: 0.6.14(@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.6.2))(prettier@3.6.2)
+ storybook:
+ specifier: ^9.1.3
+ version: 9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))
turbo:
specifier: ^2.5.6
version: 2.5.6
@@ -32,6 +59,9 @@ importers:
vite:
specifier: 7.1.2
version: 7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)
+ vitest:
+ specifier: ^3.2.4
+ version: 3.2.4(@types/node@22.15.3)(@vitest/browser@3.2.4)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)
apps/client:
dependencies:
@@ -4834,6 +4864,12 @@ snapshots:
'@types/react': 19.1.0
react: 19.1.1
+ '@mdx-js/react@3.1.0(@types/react@19.1.10)(react@19.1.1)':
+ dependencies:
+ '@types/mdx': 2.0.13
+ '@types/react': 19.1.10
+ react: 19.1.1
+
'@napi-rs/wasm-runtime@0.2.12':
dependencies:
'@emnapi/core': 1.4.5
@@ -5056,6 +5092,19 @@ snapshots:
transitivePeerDependencies:
- '@types/react'
+ '@storybook/addon-docs@9.1.3(@types/react@19.1.10)(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))':
+ dependencies:
+ '@mdx-js/react': 3.1.0(@types/react@19.1.10)(react@19.1.1)
+ '@storybook/csf-plugin': 9.1.3(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))
+ '@storybook/icons': 1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@storybook/react-dom-shim': 9.1.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ storybook: 9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))
+ ts-dedent: 2.2.0
+ transitivePeerDependencies:
+ - '@types/react'
+
'@storybook/addon-onboarding@9.1.3(storybook@9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2)))':
dependencies:
storybook: 9.1.3(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@7.1.2(@types/node@22.15.3)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.4)(yaml@1.10.2))
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 00000000..94f746d6
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,37 @@
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+import { defineConfig } from 'vitest/config';
+
+import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
+
+const dirname =
+ typeof __dirname !== 'undefined'
+ ? __dirname
+ : path.dirname(fileURLToPath(import.meta.url));
+
+// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
+export default defineConfig({
+ test: {
+ projects: [
+ {
+ extends: true,
+ plugins: [
+ // The plugin will run tests for the stories defined in your Storybook config
+ // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
+ storybookTest({ configDir: path.join(dirname, '.storybook') }),
+ ],
+ test: {
+ name: 'storybook',
+ browser: {
+ enabled: true,
+ headless: true,
+ provider: 'playwright',
+ instances: [{ browser: 'chromium' }],
+ },
+ setupFiles: ['.storybook/vitest.setup.ts'],
+ },
+ },
+ ],
+ },
+});
diff --git a/vitest.shims.d.ts b/vitest.shims.d.ts
new file mode 100644
index 00000000..f923d47d
--- /dev/null
+++ b/vitest.shims.d.ts
@@ -0,0 +1 @@
+///
\ No newline at end of file