diff --git a/.husky/pre-commit b/.husky/pre-commit index 26684dd..e02c24e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,71 +1 @@ -<<<<<<< HEAD -<<<<<<< HEAD -pnpm lint-staged -======= -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -echo "πŸ”Ž pre-commit: lint-staged μ‹€ν–‰ 및 μ΅œμ’… 점검 μ‹œμž‘" - -# 0) 컀밋에 ν¬ν•¨λœ 파일 λͺ©λ‘ (μΆ”κ°€/볡사/μˆ˜μ •) -STAGED_FILES="$(git diff --name-only --cached --diff-filter=ACMR)" -if [ -z "$STAGED_FILES" ]; then - echo "ℹ️ μŠ€ν…Œμ΄μ§•λœ 파일이 μ—†μŠ΅λ‹ˆλ‹€. κ±΄λ„ˆλœλ‹ˆλ‹€." - exit 0 -fi - -# 1) λ³€κ²½ 파일만 μžλ™ μˆ˜μ • (eslint --fix, prettier --write) -if ! pnpm lint-staged; then - echo - echo "❌ lint-staged λ‹¨κ³„μ—μ„œ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€." - echo " - μžλ™μˆ˜μ • λΆˆκ°€ν•œ 린트 μ—λŸ¬κ°€ μžˆμ„ 수 μžˆμ–΄μš”." - echo " - λ‘œμ»¬μ—μ„œ 'pnpm lint'둜 μ—λŸ¬λ₯Ό ν™•μΈν•˜κ³  μˆ˜μ •ν•œ λ’€ λ‹€μ‹œ μ»€λ°‹ν•˜μ„Έμš”." - exit 1 -fi - -# 2) μ΅œμ’… 점검: μŠ€ν…Œμ΄μ§•λœ 파일만 λŒ€μƒμœΌλ‘œ 검사(μˆ˜μ • 없이 μ‹€νŒ¨λ§Œ 감지) -PRETTIER_FAIL=0 -ESLINT_FAIL=0 - -# κ°œν–‰λ§Œ κ΅¬λΆ„μžλ‘œ μ‚¬μš© (곡백 포함 파일λͺ… μ•ˆμ „) -IFS="$(printf '\n')" -for f in $STAGED_FILES; do - case "$f" in - *.js|*.jsx|*.ts|*.tsx|*.json|*.css|*.md) - # Prettier 포맷 μ€€μˆ˜ 확인 (μˆ˜μ • 없이 검사) - pnpm -s prettier --check "$f" || PRETTIER_FAIL=1 - ;; - esac -done - -for f in $STAGED_FILES; do - case "$f" in - *.js|*.jsx|*.ts|*.tsx) - # ESLint μ΅œμ’… 확인 (μˆ˜μ • 없이 검사) - pnpm -s eslint --max-warnings=0 "$f" || ESLINT_FAIL=1 - ;; - esac -done -unset IFS - -if [ "$PRETTIER_FAIL" -ne 0 ]; then - echo - echo "❌ Prettier 포맷 μœ„λ°˜μ΄ 남아 μžˆμŠ΅λ‹ˆλ‹€." - echo " - 'pnpm format' ν›„ λ‹€μ‹œ μ»€λ°‹ν•˜μ„Έμš”." - exit 1 -fi - -if [ "$ESLINT_FAIL" -ne 0 ]; then - echo - echo "❌ ESLint μœ„λ°˜μ΄ 남아 μžˆμŠ΅λ‹ˆλ‹€." - echo " - 'pnpm lint:fix' λ˜λŠ” μˆ˜λ™ μˆ˜μ • ν›„ λ‹€μ‹œ μ»€λ°‹ν•˜μ„Έμš”." - exit 1 -fi - -COUNT="$(printf '%s\n' "$STAGED_FILES" | wc -l | tr -d ' ')" -echo "βœ… pre-commit 톡과: ${COUNT}개 파일 점검 μ™„λ£Œ" -exit 0 ->>>>>>> 382ffc3 (Chore/code style commit convention) -======= -pnpm lint-staged ->>>>>>> 103f47c (Chore/lucide react) +pnpm lint-staged \ No newline at end of file diff --git a/package.json b/package.json index 9820c94..a32def7 100644 --- a/package.json +++ b/package.json @@ -25,13 +25,15 @@ "dependencies": { "@fontsource/pretendard": "^5.2.5", "@radix-ui/react-slot": "^1.2.3", + "@tanstack/react-query": "^5.90.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.544.0", "next": "15.5.3", "react": "19.1.0", "react-dom": "19.1.0", - "tailwind-merge": "^3.3.1" + "tailwind-merge": "^3.3.1", + "zustand": "^5.0.8" }, "devDependencies": { "@commitlint/cli": "^19.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dcdee76..191062e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,6 +13,9 @@ importers: "@radix-ui/react-slot": specifier: ^1.2.3 version: 1.2.3(@types/react@19.1.13)(react@19.1.0) + "@tanstack/react-query": + specifier: ^5.90.2 + version: 5.90.2(react@19.1.0) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -34,6 +37,9 @@ importers: tailwind-merge: specifier: ^3.3.1 version: 3.3.1 + zustand: + specifier: ^5.0.8 + version: 5.0.8(@types/react@19.1.13)(react@19.1.0) devDependencies: "@commitlint/cli": specifier: ^19.8.1 @@ -875,6 +881,20 @@ packages: integrity: sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==, } + "@tanstack/query-core@5.90.2": + resolution: + { + integrity: sha512-k/TcR3YalnzibscALLwxeiLUub6jN5EDLwKDiO7q5f4ICEoptJ+n9+7vcEFy5/x/i6Q+Lb/tXrsKCggf5uQJXQ==, + } + + "@tanstack/react-query@5.90.2": + resolution: + { + integrity: sha512-CLABiR+h5PYfOWr/z+vWFt5VsOA2ekQeRQBFSKlcoW6Ndx/f8rfyVmq4LbgOM4GG2qtxAxjLYLOpCNTYm4uKzw==, + } + peerDependencies: + react: ^18 || ^19 + "@tybys/wasm-util@0.10.1": resolution: { @@ -3986,6 +4006,27 @@ packages: } engines: { node: ">=12.20" } + zustand@5.0.8: + resolution: + { + integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==, + } + engines: { node: ">=12.20.0" } + peerDependencies: + "@types/react": ">=18.0.0" + immer: ">=9.0.6" + react: ">=18.0.0" + use-sync-external-store: ">=1.2.0" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: "@alloc/quick-lru@5.2.0": {} @@ -4435,6 +4476,13 @@ snapshots: postcss: 8.5.6 tailwindcss: 4.1.13 + "@tanstack/query-core@5.90.2": {} + + "@tanstack/react-query@5.90.2(react@19.1.0)": + dependencies: + "@tanstack/query-core": 5.90.2 + react: 19.1.0 + "@tybys/wasm-util@0.10.1": dependencies: tslib: 2.8.1 @@ -6455,3 +6503,8 @@ snapshots: yocto-queue@0.1.0: {} yocto-queue@1.2.1: {} + + zustand@5.0.8(@types/react@19.1.13)(react@19.1.0): + optionalDependencies: + "@types/react": 19.1.13 + react: 19.1.0 diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 277752c..a5eaa6b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import "./globals.css"; +import { Providers } from "./providers"; export const metadata: Metadata = { title: "Custom Daily Planner", @@ -9,7 +10,23 @@ export const metadata: Metadata = { export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - {children} + + {/* + Providers둜 κ°μ‹ΈλŠ” 이유: + - React Query의 QueryClientProviderλ₯Ό μ „μ—­ 적용 + - λͺ¨λ“  ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈκ°€ λ™μΌν•œ client & cache 곡유 + - useQuery, useMutation 훅이 μ–΄λ””μ„œλ“  정상 λ™μž‘ + */} + {/* + βœ… μΆ”ν›„ ν™•μž₯ μ˜ˆμ‹œ: + + + {children} + + + */} + {children} + ); } diff --git a/src/app/providers.tsx b/src/app/providers.tsx new file mode 100644 index 0000000..201a204 --- /dev/null +++ b/src/app/providers.tsx @@ -0,0 +1,17 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactNode, useState } from "react"; + +export function Providers({ children }: { children: ReactNode }) { + const [client] = useState( + () => + new QueryClient({ + defaultOptions: { + queries: { retry: 2, refetchOnWindowFocus: false, staleTime: 30_000, gcTime: 5 * 60_000 }, + }, + }), + ); + + return {children}; +} diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index 3aca467..d2bf3da 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -9,7 +9,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) { data-slot="input" className={cn( // 크기/λ ˆμ΄μ•„μ›ƒ - "h-10 w-full min-w-0 rounded-md px-3 py-2 text-sm outline-none transition-colors", + "h-12 w-full min-w-0 rounded-md px-3 py-2 text-sm outline-none transition-colors", // κΈ°λ³Έ ν…μŠ€νŠΈ/λ°°κ²½ "bg-white text-[#111827]", // κΈ°λ³Έ ν…Œλ‘λ¦¬(1px, νšŒμƒ‰) diff --git a/src/stores/useUserStore.ts b/src/stores/useUserStore.ts new file mode 100644 index 0000000..ad19ca8 --- /dev/null +++ b/src/stores/useUserStore.ts @@ -0,0 +1,18 @@ +import { create } from "zustand"; + +type UserState = { + userId: string | null; + setUserId: (id: string | null) => void; + reset: () => void; + // μ•žμœΌλ‘œ ν™•μž₯될 수 μžˆλŠ” μ „μ—­ UI μƒνƒœ μ˜ˆμ‹œ + theme: "light" | "dark"; + toggleTheme: () => void; +}; + +export const useUserStore = create((set) => ({ + userId: null, + setUserId: (id) => set({ userId: id }), + reset: () => set({ userId: null }), + theme: "light", + toggleTheme: () => set((s) => ({ theme: s.theme === "light" ? "dark" : "light" })), +}));