diff --git a/.storybook/main.ts b/.storybook/main.ts index e93369b..2a933c1 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import type { StorybookConfig } from '@storybook/nextjs'; const config: StorybookConfig = { @@ -15,23 +16,30 @@ const config: StorybookConfig = { }, staticDirs: ['../public'], webpackFinal: async config => { - if (config.module?.rules) { - config.module = config.module || {}; - config.module.rules = config.module.rules || []; + config.module = config.module || {}; + config.module.rules = config.module.rules || []; + const rules = config.module.rules; - const imageRule = config.module.rules.find(rule => - rule?.['test']?.test('.svg'), - ); - if (imageRule) { - imageRule['exclude'] = /\.svg$/; - } - - config.module.rules.push({ - test: /\.svg$/, - use: ['@svgr/webpack'], - }); + // 기존 이미지/svg 처리 rule에서 svg 제외 + const imageRule = rules.find((rule: any) => rule?.test?.test?.('.svg')); + if (imageRule) { + imageRule.exclude = /\.svg$/i; } + // SVG 처리: 기본은 URL(= next/image에 넣을 수 있음) + rules.push({ + test: /\.svg$/i, + oneOf: [ + { + resourceQuery: /component/, // import Icon from './x.svg?component' + use: ['@svgr/webpack'], + }, + { + type: 'asset/resource', // import url from './x.svg' + }, + ], + }); + return config; }, }; diff --git a/.storybook/preview.ts b/.storybook/preview.ts index b0c0be0..6e313b4 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,7 +1,18 @@ import type { Preview } from '@storybook/nextjs-vite'; import '../src/app/globals.css'; +import { pretendard } from '../src/lib/fonts/pretendard'; const preview: Preview = { + decorators: [ + Story => { + // pretendard 폰트 적용 + if (typeof document !== 'undefined') { + document.documentElement.classList.add(pretendard.className); + } + + return Story(); + }, + ], parameters: { controls: { matchers: { @@ -9,13 +20,7 @@ const preview: Preview = { 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', - }, + a11y: { test: 'todo' }, }, }; diff --git a/package.json b/package.json index 6316e52..617441e 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@hookform/resolvers": "^5.2.2", "clsx": "^2.1.1", "globals": "^16.4.0", + "motion": "^12.23.26", "immer": "^10.1.3", "next": "15.5.3", "react": "19.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5629aeb..38b922d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: globals: specifier: ^16.4.0 version: 16.4.0 + motion: + specifier: ^12.23.26 + version: 12.23.26(react-dom@19.1.0(react@19.1.0))(react@19.1.0) immer: specifier: ^10.1.3 version: 10.1.3 @@ -2989,6 +2992,20 @@ packages: typescript: '>3.6.0' webpack: ^5.11.0 + framer-motion@12.23.26: + resolution: {integrity: sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -3653,6 +3670,26 @@ packages: module-alias@2.2.3: resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==} + motion-dom@12.23.23: + resolution: {integrity: sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==} + + motion-utils@12.23.6: + resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} + + motion@12.23.26: + resolution: {integrity: sha512-Ll8XhVxY8LXMVYTCfme27WH2GjBrCIzY4+ndr5QKxsK+YwCtOi2B/oBi5jcIbik5doXuWT/4KKDOVAZJkeY5VQ==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -8148,6 +8185,15 @@ snapshots: typescript: 5.9.2 webpack: 5.101.3(esbuild@0.25.10) + framer-motion@12.23.26(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + motion-dom: 12.23.23 + motion-utils: 12.23.6 + tslib: 2.8.1 + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -8781,6 +8827,20 @@ snapshots: module-alias@2.2.3: {} + motion-dom@12.23.23: + dependencies: + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} + + motion@12.23.26(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + framer-motion: 12.23.26(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + tslib: 2.8.1 + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + mrmime@2.0.1: {} ms@2.1.3: {} diff --git a/public/ex.png b/public/ex.png new file mode 100644 index 0000000..b17c649 Binary files /dev/null and b/public/ex.png differ diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5c22722..9ae88ce 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next'; import { Geist, Geist_Mono } from 'next/font/google'; import { pretendard } from '@/lib/fonts/pretendard'; import './globals.css'; +import { Suspense } from 'react'; const geistSans = Geist({ variable: '--font-geist-sans', @@ -35,7 +36,9 @@ export default function RootLayout({ pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)] " > -