๊ฐ์ธ ์ฌ๋ฌด ๊ด๋ฆฌ๋ฅผ ์ํ ์ง์ถ ์ถ์ ๋ฐ ๋ถ์ ๊ฐ๊ณ๋ถ ์น ์ ํ๋ฆฌ์ผ์ด์
Blockie๋ ์ฌ์ฉ์์ ์ง์ถ์ ์๊ฐ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ๋ถ์ํ ์ ์๋ ํ์คํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋ค. ์ผ์ผ ์ง์ถ ๊ธฐ๋ก์ ์์ ํ๋ธ๋ก ์๊ฐํํ๊ณ , ์ฐ์ ๊ธฐ๋ก ์คํธ๋ฆญ(Streak) ์์คํ ์ ํตํด ๊พธ์คํ ์ฌ๋ฌด ๊ด๋ฆฌ๋ฅผ ๋ ๋ คํฉ๋๋ค.
ํ๋ก๋ํธ ์์ฑ๋๋ณด๋ค๋ ๊พธ์คํ ๊ธฐ์ ์ ๋์ ๊ณผ ํ์ต ๊ฒฝํ์ ์ค์ ์ ๋๊ณ , ํด๊ทผ ํ ๊ณต๋ถํ ๊ฒ์ ๋ง๋ถ์ฌ ๊ฐ๋ฐํ๋ฉฐ ๊น์ด ์๊ฒ ์ดํดํ๋ ๊ฒ์ ๋ชฉํ๋ก ํ์ต๋๋ค. ์ผ์ผ ์ง์ถ ๊ธฐ๋ก์ ์์ ํ๋ธ๋ก ์๊ฐํํ๋ ์ปจ์ ์ ํตํด ๋ค์ํ ๊ธฐ์ ์์ญ(ํ๋ก ํธ์๋, ๋ฐฑ์๋, ๋ชจ๋ ธ๋ ํฌ, ์ ๋๋ฉ์ด์ , ์ํ ๊ด๋ฆฌ ๋ฑ)์ ์ข ํฉ์ ์ผ๋ก ๊ฒฝํํ๋ฉฐ ๊ฐ๋ฐํ์ต๋๋ค.
- ์ง์ถ ๊ด๋ฆฌ: ์นดํ ๊ณ ๋ฆฌ๋ณ ์ง์ถ ์ถ์ ๋ฐ ํต๊ณ ๋ถ์
- ์์ฐ ์ค์ : ์๋ณ ์์ฐ ์ค์ ๋ฐ ์ค์๊ฐ ์์ฐ ๋๋น ์ง์ถ ๋ชจ๋ํฐ๋ง
- ์๊ฐ์ ๋์๋ณด๋: ์ง์ถ ๋ด์ญ์ ์์ ํ๋ธ๋ก ํํํ๋ ๋ ์ฐฝ์ ์ธ UI
- ์คํธ๋ฆญ ์์คํ : ์ฐ์ ๊ธฐ๋ก ์ผ์ ์ถ์ ์ผ๋ก ์ต๊ด ํ์ฑ ์ง์
- ๋ค๊ตญ์ด ์ง์: ํ๊ตญ์ด, ์์ด, ์ค๊ตญ์ด ์ง์
- ์ค์๊ฐ ๋ถ์: ์ผ๊ฐ/์ฃผ๊ฐ/์๊ฐ ์ง์ถ ํจํด ๋ถ์ ๋ฐ ์ธ์ฌ์ดํธ ์ ๊ณต
- ์ฌ์ฉ์ ์ธ์ฆ์ด ํ์ํ ํ์ด์ง๋ฅผ ์ด๋ป๊ฒ ๋น ๋ฅด๊ณ ์์ ํ๊ฒ ๋ ๋๋งํ ๊น?
- ์ง์ถ ๋ธ๋ก ์๊ฐํ์์ ๋ฐ์ดํฐ ์ ํฉ์ฑ ๋ฌธ์ ํด๊ฒฐ ์ฌ์
- ๋ค๊ตญ์ด ์๋น์ค์์ ์ค์ฉ์ ์ธ ์ฌ์ฉ์ ์ค์ฌ ์๋ฌ ์ฒ๋ฆฌ
- ๊ท๋ชจ์ ๋ง๋ ์ ๋ง ํ์ค์ ์ธ ๋ก๊น ์์คํ ๊ตฌ์ถ๊ธฐ
- ๊ณ ๊ตฐ๋ถํฌ ๋ชจ๋ ธ๋ ํฌ ๋ฐฐํฌ 1
- ๊ณ ๊ตฐ๋ถํฌ ๋ชจ๋ ธ๋ ํฌ ๋ฐฐํฌ 2
[์ง์ถ ๋ฑ๋ก ํ๋ฉด] [ํ๋ธ ๋์๋ณด๋] [์์ฐ ์ค์ ] [ํต๊ณ ๋ถ์]
- Framework: Next.js 15 (App Router) + React 19
- Language: TypeScript 5.8
- Styling: Tailwind CSS 4.0
- State Management: Zustand 5.0
- Data Fetching: TanStack React Query 5.8
- Animation: Framer Motion 12.23
- Charts: Chart.js, React-ChartJS-2, Lightweight Charts
- i18n: next-intl
- Framework: NestJS 11.0
- Language: TypeScript 5.8
- Database: PostgreSQL + Prisma ORM 6.9
- Authentication: JWT + Passport.js
- Validation: Class Validator
- API Documentation: Swagger/OpenAPI
- Monorepo: Turborepo 2.5
- Package Manager: npm 10.2.0
- Testing: Jest 30.1 + React Testing Library
- Build Tool: Turbopack (Next.js), tsup (UI library)
- Code Quality: ESLint, TypeScript strict mode
color-expense/
โโโ apps/
โ โโโ web/ # Next.js ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์
โ โโโ api/ # NestJS ๋ฐฑ์๋ API ์๋ฒ
โ โโโ docs/ # ํ๋ก์ ํธ ๋ฌธ์
โโโ packages/
โ โโโ ui/ # ๊ณต์ React UI ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
โ โโโ types/ # ๊ณต์ TypeScript ํ์
์ ์
โ โโโ eslint-config/ # ESLint ์ค์
โ โโโ typescript-config/# TypeScript ์ค์
โโโ turbo.json # Monorepo ๋น๋ ์ค์
web/
โโโ app/[locale]/ # Next.js App Router (๋ค๊ตญ์ด ์ง์)
โ โโโ (public)/ # ๊ณต๊ฐ ๋ผ์ฐํธ (๋ก๊ทธ์ธ, ํ์๊ฐ์
)
โ โโโ (private)/ # ์ธ์ฆ ํ์ ๋ผ์ฐํธ
โ โโโ expense/ # ์ง์ถ ๊ด๋ฆฌ
โ โโโ budget/ # ์์ฐ ์ค์
โ โโโ cube/ # ํ๋ธ ๋์๋ณด๋
โ โโโ mypage/ # ๋ง์ดํ์ด์ง
โ โโโ onboarding/ # ์จ๋ณด๋ฉ
โโโ @component/
โ โโโ features/ # ๊ธฐ๋ฅ๋ณ ์ปดํฌ๋ํธ
โโโ @hook/
โ โโโ api/ # API ์ฐ๋ ํ
โ โโโ business/ # ๋น์ฆ๋์ค ๋ก์ง ํ
โโโ @utils/ # ์ ํธ๋ฆฌํฐ ํจ์
โโโ @store/ # Zustand ์ํ ๊ด๋ฆฌ
โโโ messages/ # i18n ๋ฒ์ญ ํ์ผ
api/src/
โโโ auth/ # ์ธ์ฆ ๋ชจ๋ (JWT)
โโโ users/ # ์ฌ์ฉ์ ๊ด๋ฆฌ
โโโ expenses/ # ์ง์ถ CRUD ๋ฐ ํต๊ณ
โโโ budget/ # ์์ฐ ๊ด๋ฆฌ
โโโ prisma/ # Prisma ์คํค๋ง ๋ฐ ๋ง์ด๊ทธ๋ ์ด์
ui/
โโโ src/
โ โโโ button/ # ๋ฒํผ ์ปดํฌ๋ํธ
โ โโโ card/ # ์นด๋ ์ปดํฌ๋ํธ
โ โโโ animation/ # ์ ๋๋ฉ์ด์
์ ํธ
โโโ storybook/ # Storybook ์ค์
model User {
id String @id @default(cuid())
name String
email String @unique
phone String
expenses Expense[]
budgets Budget[]
createdAt DateTime @default(now())
}model Expense {
id String @id @default(cuid())
userId String
amount Int
category String
expenseDate DateTime // ์ค์ ์ง์ถ ๋ ์ง
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
}model Budget {
id String @id @default(cuid())
userId String
year Int
month Int
amount Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
@@unique([userId, year, month])
}POST /auth/signin- ๋ก๊ทธ์ธPOST /auth/signup- ํ์๊ฐ์POST /auth/refresh- ํ ํฐ ๊ฐฑ์
POST /expenses- ์ง์ถ ์์ฑGET /expenses- ์ง์ถ ๋ชฉ๋ก ์กฐํPATCH /expenses/:id- ์ง์ถ ์์ DELETE /expenses/:id- ์ง์ถ ์ญ์ GET /expenses/stats/daily- ์ผ์ผ ํต๊ณGET /expenses/stats/weekly- ์ฃผ๊ฐ ํต๊ณGET /expenses/stats/monthly- ์๊ฐ ํต๊ณGET /expenses/stats/category- ์นดํ ๊ณ ๋ฆฌ๋ณ ๋ถ์GET /expenses/stats/streak- ์ฐ์ ๊ธฐ๋ก ํต๊ณGET /expenses/analysis- ํธ๋ ๋ ๋ถ์
POST /budget- ์์ฐ ์์ฑGET /budget- ์์ฐ ์กฐํPATCH /budget/:id- ์์ฐ ์์ GET /budget/recommendation- ์์ฐ ์ถ์ฒ
- Node.js >= 18
- npm 10.2.0
- PostgreSQL
# ์ ์ฅ์ ํด๋ก
git clone https://github.com/JangAyeon/color-expense.git
cd color-expense
# ์์กด์ฑ ์ค์น
npm installDATABASE_URL="postgresql://user:password@localhost:5432/color_expense"
JWT_SECRET="your-jwt-secret"
JWT_EXPIRES_IN="7d"NEXT_PUBLIC_API_URL="http://localhost:3001"
NEXT_PUBLIC_SUPABASE_URL="your-supabase-url"
NEXT_PUBLIC_SUPABASE_ANON_KEY="your-supabase-key"cd apps/api
npx prisma migrate dev
npx prisma generate# ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์
์คํ (monorepo)
npm run dev
# ๊ฐ๋ณ ์คํ
npm run dev:web # ํ๋ก ํธ์๋๋ง ์คํ (http://localhost:3000)
npm run dev:api # ๋ฐฑ์๋๋ง ์คํ (http://localhost:3001)# ์ ์ฒด ๋น๋
npm run build
# ํ์
์ฒดํฌ
npm run check-types
# ๋ฆฐํธ
npm run lint
# ํ
์คํธ
npm run test- Turborepo๋ฅผ ํ์ฉํ ํจ์จ์ ์ธ ๋น๋ ์บ์ฑ ๋ฐ ๋ณ๋ ฌ ์คํ
- ๊ณต์ UI ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ์ ์ ์๋ฅผ ํตํ ์ฝ๋ ์ฌ์ฌ์ฉ
- ์ผ๊ด๋ ESLint ๋ฐ TypeScript ์ค์ ๊ณต์
apps/web/@component/features/cube
- Framer Motion์ ํ์ฉํ ์ ๋๋ฉ์ด์ ํ๋ธ ๊ทธ๋ฆฌ๋
- ์ง์ถ ๊ธ์ก์ ๋ฐ๋ฅธ ๋์ ์์ ํํ
- ์ฐ์ ๊ธฐ๋ก ์คํธ๋ฆญ ์๊ฐํ
- React Query๋ฅผ ํ์ฉํ ์๋ฒ ์ํ ๊ด๋ฆฌ
- Optimistic Updates๋ก ๋น ๋ฅธ UX ์ ๊ณต
- ์๋ ๋ฆฌํจ์นญ ๋ฐ ์บ์ ๋ฌดํจํ ์ ๋ต
- next-intl์ ํ์ฉํ ์ ์ ๋ฉ์์ง ๋ฒ์ญ
- URL ๊ธฐ๋ฐ ๋ก์ผ์ผ ๋ผ์ฐํ
(
/ko,/en,/zh) - ๋์ ๋ก์ผ์ผ ์ ํ UI
- Tailwind CSS๋ฅผ ํ์ฉํ ๋ชจ๋ฐ์ผ ์ฐ์ ๋์์ธ
- ํ๋ธ๋ฆฟ ๋ฐ ๋ฐ์คํฌํฑ ํ๊ฒฝ ์ต์ ํ
- ํฐ์น ์ ์ค์ฒ ์ง์
- ์๊ฒฉํ TypeScript ์ค์ (
strict: true) - Prisma๋ฅผ ํตํ ํ์ ์์ ํ DB ์ฟผ๋ฆฌ
- ํ๋ก ํธ์๋-๋ฐฑ์๋ ๊ฐ ํ์
๊ณต์ (
@repo/types)
- Jest ๊ธฐ๋ฐ ๋จ์ ํ ์คํธ
- React Testing Library๋ฅผ ํ์ฉํ ์ปดํฌ๋ํธ ํ ์คํธ
- ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง ๋ฆฌํฌํธ
- ์ฝ๋ ์คํ๋ฆฌํ : Next.js ๋์ import ํ์ฉ
- ์ด๋ฏธ์ง ์ต์ ํ: Next.js Image ์ปดํฌ๋ํธ
- ๋น๋ ์ต์ ํ: Turbopack์ ํตํ ๋น ๋ฅธ ๊ฐ๋ฐ ์๋ฒ
- ์บ์ฑ ์ ๋ต: React Query์ staleTime ๋ฐ cacheTime ์ค์
- ๋ฒ๋ค ์ฌ์ด์ฆ ์ต์ ํ: Tree shaking ๋ฐ ๋์ import
- PWA ์ง์ (์คํ๋ผ์ธ ๋ชจ๋)
- ์ง์ถ ์๋ฆผ ๊ธฐ๋ฅ (ํธ์ ์๋ฆผ)
- AI ๊ธฐ๋ฐ ์ง์ถ ์์ธก ๋ฐ ์ถ์ฒ
- ์์์ฆ OCR ์ค์บ ๊ธฐ๋ฅ
- ์์ ๊ณต์ ๊ธฐ๋ฅ (์ง์ถ ํต๊ณ ๊ณต์ )
- ๋คํฌ ๋ชจ๋ ์ง์
- E2E ํ ์คํธ ์ถ๊ฐ (Playwright)
- ๋ชจ๋ ธ๋ ํฌ ๊ด๋ฆฌ: Turborepo๋ฅผ ํตํ ํจ์จ์ ์ธ ๋ฉํฐ ํจํค์ง ๊ด๋ฆฌ ๊ฒฝํ
- ํ์คํ ๊ฐ๋ฐ: Next.js์ NestJS๋ฅผ ์ฐ๋ํ End-to-End ๊ฐ๋ฐ ๊ฒฝํ
- ํ์ ์์ ์ฑ: TypeScript๋ฅผ ํ์ฉํ ํ์ ๊ธฐ๋ฐ ๊ฐ๋ฐ ํ๋ก์ธ์ค ํ๋ฆฝ
- ์ํ ๊ด๋ฆฌ: React Query์ Zustand๋ฅผ ์กฐํฉํ ํจ์จ์ ์ธ ์ํ ๊ด๋ฆฌ ์ ๋ต
- ๊ตญ์ ํ: next-intl์ ํ์ฉํ ๋ค๊ตญ์ด ์ง์ ๊ตฌํ