diff --git a/CLAUDE.md b/CLAUDE.md index e5cb5a5..f3e975e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -161,15 +161,27 @@ src/app/(page)/*/*.store.ts `features` 폴더는 사용하지 않는다. +### MUI 사용 허용 범위 + +```txt +허용: +- 달력 / 날짜 선택 UI: src/components/calendar/ 내 래핑 컴포넌트 +- 피드백 UI (Toast/Snackbar): src/components/feedback/ 내 래핑 컴포넌트 +- 아이콘: @mui/icons-material — 감정일기(diary) 기능 제외 전체 허용 + +금지: +- 감정일기(diary) 기능 내 아이콘은 lucide-react 사용 유지 +- 전체 디자인 시스템을 MUI 기반으로 전환하지 않는다. +- 여러 페이지 / 도메인에서 MUI 컴포넌트를 직접 import하지 않는다. +- MUI ThemeProvider를 앱 전역에 적용하지 않는다. +- 전체 스타일 기준은 SCSS / SCSS Module을 유지한다. +``` + ### Calendar UI Rules ```txt - 달력 UI는 MUI X Date Pickers 기반으로 구현한다. -- MUI는 달력 / 날짜 선택 UI 전용으로만 사용한다. 전체 디자인 시스템에 적용하지 않는다. -- 전체 스타일 기준은 SCSS / SCSS Module을 유지한다. - MUI 컴포넌트는 src/components/calendar/ 내 래핑 컴포넌트를 통해서만 사용한다. -- 여러 페이지 / 도메인에서 MUI 컴포넌트를 직접 import하지 않는다. -- 아이콘은 기존 lucide-react를 우선 사용한다. - 달력 라이브러리는 래핑 컴포넌트 구조로 교체 가능성을 확보한다. ``` diff --git a/docs/PROJECT_DECISIONS.md b/docs/PROJECT_DECISIONS.md index 1c8951e..a64d2e3 100644 --- a/docs/PROJECT_DECISIONS.md +++ b/docs/PROJECT_DECISIONS.md @@ -227,6 +227,6 @@ SUPABASE_SERVICE_ROLE_KEY는 서버 전용 - 전체 디자인 시스템은 SCSS / SCSS Module을 유지한다. - MUI는 달력 / 날짜 선택 UI 전용으로만 사용한다. - MUI 컴포넌트는 src/components/calendar/ 내 래핑 컴포넌트를 통해서만 사용한다. -- 아이콘은 기존 lucide-react를 우선 사용한다. +- 아이콘은 @mui/icons-material을 사용한다. 단, 감정일기(diary) 기능 내 아이콘은 lucide-react를 유지한다. - 달력 라이브러리는 래핑 컴포넌트 구조로 교체 가능성을 확보한다. ``` diff --git a/docs/TECH_STACK.md b/docs/TECH_STACK.md index cc4a7b7..6a99164 100644 --- a/docs/TECH_STACK.md +++ b/docs/TECH_STACK.md @@ -39,8 +39,7 @@ dayjs - 전체 디자인 시스템을 MUI 기반으로 전환하지 않는다. - 기존 스타일 기준은 SCSS / SCSS Module을 유지한다. -- 아이콘은 기존 `lucide-react` 사용을 우선한다. -- `@mui/icons-material`은 현재 필수 설치 대상이 아니다. +- 아이콘은 `@mui/icons-material`을 사용한다. 단, 감정일기(diary) 기능 내 아이콘은 `lucide-react`를 유지한다. - 달력 라이브러리는 추후 변경 가능하다. 변경 가능성을 확보하기 위해 MUI 의존성은 Calendar 전용 컴포넌트와 날짜 유틸 영역에만 제한한다. --- @@ -76,7 +75,8 @@ npm install | `react-hook-form` | ^7.54.1 | 폼 상태 관리 | | `@hookform/resolvers` | ^3.9.1 | React Hook Form + Zod 연동 | | `zod` | ^3.23.8 | 스키마 유효성 검사 | -| `lucide-react` | ^0.468.0 | 아이콘 라이브러리 | +| `lucide-react` | ^0.468.0 | 아이콘 라이브러리 (감정일기 기능 전용 — 그 외는 @mui/icons-material 사용) | +| `@mui/icons-material` | latest | MUI 아이콘 (감정일기 기능 제외 전체 사용) | | `recharts` | ^3.0.0 | 차트 라이브러리 (통계 시각화) | | `html-to-image` | ^1.11.11 | 판결 결과 카드 이미지 추출 | | `sass` | ^1.83.0 | SCSS / SCSS Module 스타일링 | diff --git a/docs/guides/CODING_CONVENTION.md b/docs/guides/CODING_CONVENTION.md index 183d803..44dbf06 100644 --- a/docs/guides/CODING_CONVENTION.md +++ b/docs/guides/CODING_CONVENTION.md @@ -90,16 +90,18 @@ API 응답에 불필요한 민감 정보 포함 금지 ## 7. MUI 사용 기준 -MUI는 달력 / 날짜 선택 UI 전용으로만 사용한다. +MUI는 아래 허용 범위 내에서만 사용한다. ```txt 허용 범위: - src/components/calendar/ 내 래핑 컴포넌트에서만 MUI import 허용 + src/components/calendar/ → MUI X Date Pickers (달력 / 날짜 선택 UI) + src/components/feedback/ → MUI Snackbar 등 (Toast 피드백 UI) + @mui/icons-material → 감정일기(diary) 기능 제외 전체 허용 금지 사항: + - 감정일기(diary) 기능 내 아이콘은 lucide-react 사용 유지 - 전체 디자인 시스템을 MUI 기반으로 전환하지 않는다 - 여러 페이지 / 도메인에서 MUI 컴포넌트를 직접 import하지 않는다 - - @mui/icons-material 사용 금지 (아이콘은 lucide-react 사용) - MUI ThemeProvider를 앱 전역에 적용하지 않는다 ``` @@ -114,7 +116,7 @@ MUI는 달력 / 날짜 선택 UI 전용으로만 사용한다. 래핑 컴포넌트 원칙: ```txt -- 달력 UI는 src/components/calendar/ 내 래핑 컴포넌트로 제공한다 +- MUI 컴포넌트는 각 허용 폴더 내 래핑 컴포넌트를 통해서만 사용한다 - 래핑 컴포넌트를 통해 라이브러리 교체 가능성을 유지한다 - 래핑 컴포넌트 외부에서는 MUI 직접 의존을 갖지 않는다 ``` diff --git a/package-lock.json b/package-lock.json index a6b3aa8..271029e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@emotion/styled": "^11.14.1", "@google/generative-ai": "^0.21.0", "@hookform/resolvers": "^3.9.1", + "@mui/icons-material": "^9.1.1", "@mui/material": "^9.1.1", "@mui/x-date-pickers": "^9.5.0", "@prisma/client": "^6.0.0", @@ -181,6 +182,29 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", @@ -188,6 +212,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -250,7 +275,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -294,7 +318,6 @@ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1079,6 +1102,32 @@ "url": "https://opencollective.com/mui-org" } }, + "node_modules/@mui/icons-material": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-9.1.1.tgz", + "integrity": "sha512-OXhm9DajemStb58AumM06DuPhHTa3XD36TFD4yf6WtJyNRO5DfEZbbnHlBg/US2Y2oOXwM/XurMTBOD6L/YYZw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^9.1.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/material": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@mui/material/-/material-9.1.1.tgz", @@ -1200,7 +1249,6 @@ "resolved": "https://registry.npmjs.org/@mui/system/-/system-9.1.1.tgz", "integrity": "sha512-q+aqNa58QZUwmmyUvJKKrStrej+4BcWFw4M0Ug+zRylPIQgR64cqvBnE3QTfLZm4OXulydp8Hl3zwKxMayrdsA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.29.2", "@mui/private-theming": "^9.1.1", @@ -2292,7 +2340,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2367,7 +2414,6 @@ "integrity": "sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.61.0", "@typescript-eslint/types": "8.61.0", @@ -2982,7 +3028,6 @@ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3989,8 +4034,7 @@ "version": "1.11.21", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", @@ -4457,7 +4501,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4631,7 +4674,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6128,7 +6170,6 @@ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "devOptional": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -6696,7 +6737,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-15.5.19.tgz", "integrity": "sha512-xNOW6tYshGX1/Oi3F8uuk4gpDeWsSUE/1Z0G5uUMekIxaQ0xc03UXd9II0VQHYMWviMeA0OHpJFAKsHf8bTYVg==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "15.5.19", "@swc/helpers": "0.5.15", @@ -7303,7 +7343,6 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz", "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -7366,7 +7405,6 @@ "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "6.19.3", "@prisma/engines": "6.19.3" @@ -7517,7 +7555,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7527,7 +7564,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -7540,7 +7576,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.79.0.tgz", "integrity": "sha512-mhYp/MTmXvzYX6AJcJVko0rktoIhhmRnEouObj4wF5i/tCttgJvnp1+9wRkpITZjDTqpo4IOSJqu0dBlPlV/Lw==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -7556,15 +7591,13 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-redux": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz", "integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -7647,8 +7680,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -7885,7 +7917,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^5.0.0", "immutable": "^5.1.5", @@ -8562,7 +8593,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8736,7 +8766,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 250db8e..7836406 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@emotion/styled": "^11.14.1", "@google/generative-ai": "^0.21.0", "@hookform/resolvers": "^3.9.1", + "@mui/icons-material": "^9.1.1", "@mui/material": "^9.1.1", "@mui/x-date-pickers": "^9.5.0", "@prisma/client": "^6.0.0", diff --git a/src/components/feedback/Toast.module.scss b/src/components/feedback/Toast.module.scss index 65105f8..44a3e4b 100644 --- a/src/components/feedback/Toast.module.scss +++ b/src/components/feedback/Toast.module.scss @@ -2,12 +2,8 @@ @use '../../styles/abstracts/functions' as fn; .toast { - position: fixed; - top: fn.r(20); - left: 50%; - transform: translateX(-50%); - z-index: 9999; - width: calc(100% - fn.r(32)); + width: calc(100vw - fn.r(32)); + max-width: calc(v.$container-max-width - fn.r(32)); padding: fn.r(14) fn.r(20); background-color: v.$color-black-600; color: var(--text-inverse); @@ -17,11 +13,6 @@ font-weight: v.$font-weight-medium; line-height: v.$line-height-body-s; text-align: center; - pointer-events: none; word-break: keep-all; overflow-wrap: break-word; - - &--hidden { - display: none; - } } diff --git a/src/components/feedback/Toast.tsx b/src/components/feedback/Toast.tsx index c236dd0..a386434 100644 --- a/src/components/feedback/Toast.tsx +++ b/src/components/feedback/Toast.tsx @@ -1,18 +1,22 @@ 'use client'; +import Snackbar from '@mui/material/Snackbar'; import { useToastStore } from '@/stores/toastStore'; import styles from './Toast.module.scss'; export default function Toast() { - const { message, isVisible } = useToastStore(); + const { message, isVisible, hide } = useToastStore(); return ( -
- {message} -
+
+ {message} +
+ ); } diff --git a/src/components/layout/BottomNavigation.tsx b/src/components/layout/BottomNavigation.tsx index f750dd4..237637c 100644 --- a/src/components/layout/BottomNavigation.tsx +++ b/src/components/layout/BottomNavigation.tsx @@ -2,14 +2,17 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; -import { Home, BookOpenText, CalendarDays, User } from 'lucide-react'; +import HomeRoundedIcon from '@mui/icons-material/HomeRounded'; +import GavelRoundedIcon from '@mui/icons-material/GavelRounded'; +import CalendarMonthRoundedIcon from '@mui/icons-material/CalendarMonthRounded'; +import PersonRoundedIcon from '@mui/icons-material/PersonRounded'; import styles from './BottomNavigation.module.scss'; const NAV_ITEMS = [ - { href: '/home', label: '홈', icon: Home }, - { href: '/records', label: '사건기록', icon: BookOpenText }, - { href: '/diary', label: '일기', icon: CalendarDays }, - { href: '/mypage', label: '마이페이지', icon: User }, + { href: '/home', label: '홈', Icon: HomeRoundedIcon }, + { href: '/records', label: '사건기록', Icon: GavelRoundedIcon }, + { href: '/diary', label: '일기', Icon: CalendarMonthRoundedIcon }, + { href: '/mypage', label: '마이페이지', Icon: PersonRoundedIcon }, ] as const; export default function BottomNavigation() { @@ -18,7 +21,7 @@ export default function BottomNavigation() { return (