17
전체 Task
17
완료 Task
4
개발 Phase
100%
전체 완료율
⚡
Next.js 16
App Router
🔷
TypeScript
strict 모드
🎨
shadcn/ui
Tailwind CSS
🗄️
Prisma 5
ORM · MySQL
🐬
MariaDB
외부 호스팅
🔐
jose
JWT · Edge
📊
ExcelJS
xlsx 업로드
🔄
PM2
포트 3003
Phase 1 — 애플리케이션 골격 구축
4 / 4 Task · 100%
Phase 2 — UI/UX 완성 (더미 데이터)
4 / 4 Task · 100%
Phase 3 — 핵심 기능 구현
6 / 6 Task · 100%
Phase 4 — 통합 테스트 및 배포 준비
3 / 3 Task · 100%
P1
Phase 1 — 애플리케이션 골격 구축
Week 1 · 약 5일 · Next.js 초기 셋업, DB 스키마, 라우트 골격, 타입 정의
| Task | 작업 내용 | 주요 파일 | 상태 |
|---|---|---|---|
| 001 | 프로젝트 초기 셋업 및 환경 구성 Next.js 16 · TypeScript strict · Tailwind CSS · shadcn/ui · ESLint · Prettier · .env.example | package.jsontsconfig.json | ✅ 완료 |
| 002 | Prisma 스키마 및 DB 연결 구성 users · point_histories 모델 · remaining_amount · MariaDB 연결 검증 | schema.prismalib/db.ts | ✅ 완료 |
| 003 | 라우트 골격 및 레이아웃 구조 생성 App Router 그룹 라우팅 · (auth) · (user)/dashboard · admin/* · 루트 리다이렉트 | app/(auth)/app/admin/ | ✅ 완료 |
| 004 | TypeScript 타입 정의 및 공통 유틸리티 User · PointHistory · Session · ApiResponse<T> · PaginatedResult<T> · getSession/requireUser/requireAdmin | types/index.tslib/auth.ts | ✅ 완료 |
P2
Phase 2 — UI/UX 완성 (더미 데이터)
Week 2 · 약 5일 · 실제 API 없이 모든 페이지 UI 완성
| Task | 작업 내용 | 주요 파일 | 상태 |
|---|---|---|---|
| 005 | shadcn/ui 컴포넌트 설치 및 디자인 시스템 button · input · card · table · dialog · form · toast · badge 설치 · 더미 데이터 팩토리 · format.ts 유틸 | components/ui/lib/format.ts | ✅ 완료 |
| 006 | 인증 페이지 UI (로그인 / 회원가입 / 관리자 로그인) react-hook-form · zod 클라이언트 검증 · aria-describedby 접근성 · LogoutButton 클라이언트 분리 | login-form.tsxregister-form.tsx | ✅ 완료 |
| 007 | 사용자 대시보드 UI (잔여액 + 상세 내역) 잔여 포인트 카드 · 내역 테이블 · URL searchParams 페이지네이션 · 모바일 카드 리스트 반응형 | dashboard/page.tsxpagination.tsx | ✅ 완료 |
| 008 | 관리자 페이지 UI (회원 목록 + 차감 모달 + 엑셀 업로드) 회원 목록 테이블 · 검색/페이지네이션 · 차감 Dialog 모달 · 파일 드롭존(5MB/10,000행) | members-client.tsxupload-zone.tsx | ✅ 완료 |
P3
Phase 3 — 핵심 기능 구현
Week 3~5 · 약 15일 · 더미 데이터를 실제 DB/API로 대체
| Task | 작업 내용 | 주요 파일 | 상태 |
|---|---|---|---|
| 009 | 회원 인증 API 및 세션 관리 POST register · login · logout · bcryptjs 해싱 · SignJWT 8h · HttpOnly Secure Cookie · zod 입력 검증 | api/auth/lib/auth.ts | ✅ 완료 |
| 010 | Middleware 라우트 보호 + 이중 권한 검증 jose jwtVerify Edge Runtime · /dashboard·/admin/* 보호 · requireAdmin() 이중 검증 · api-helpers.ts 공통 에러 응답 | middleware.tsapi-helpers.ts | ✅ 완료 |
| 011 | 포인트 잔여액 / 내역 조회 API getBalance: SUM(remaining_amount) WHERE type=grant AND expires_at>NOW() · getHistory 페이지네이션 · SSR | lib/points.tsapi/points/ | ✅ 완료 |
| 012 | 관리자 회원 목록 조회 API GET /api/admin/members · 검색(이름/이메일) · 페이지네이션 · groupBy 잔여 포인트 집계(N+1 방지) | api/admin/members/ | ✅ 완료 |
| 013 | 관리자 포인트 차감 (FIFO + Serializable 트랜잭션) POST /api/admin/points/deduct · expiresAt ASC FIFO · Prisma.$transaction Serializable · deduct 로그 INSERT | api/admin/points/deduct/ | ✅ 완료 |
| 014 | 엑셀 데이터 업로드 (ExcelJS + 단일 트랜잭션) POST /api/admin/upload · 5MB/10,000행 · exceljs 파싱 · 이메일·amount 검증 · $transaction INSERT · 중복 방지 | api/admin/upload/upload-zone.tsx | ✅ 완료 |
P4
Phase 4 — 통합 테스트 및 배포 준비
Week 6~7 · 약 7일 · 전체 플로우 E2E 검증 · 보안 강화 · 운영 준비
| Task | 작업 내용 | 주요 파일 | 상태 |
|---|---|---|---|
| 015 | E2E 통합 테스트 (Playwright MCP) 회원 풀플로우 · 관리자 풀플로우 · 권한 시나리오 · 3개 뷰포트(375/768/1280px) · 미들웨어 버그 2건 수정 | middleware.ts | ✅ 완료 |
| 016 | 보안 강화 (Rate Limit + Env 검증 + Admin 로그) rate-limit.ts · env.ts(zod) · admin-logger.ts(logs/admin-access.log) · rateLimitResponse() 429 | lib/rate-limit.tslib/env.ts | ✅ 완료 |
| 017 | 배포 준비 및 운영 문서화 prisma/seed.ts · README.md 재작성 · .env.example 5개 키 · pnpm build warning 0건 | prisma/seed.tsREADME.md | ✅ 완료 |
Phase 3 코드 리뷰 → 반영 완료
Critical
KST 타임존 오프셋 버그 —
addOneYearKST()의 수동 +9h 오프셋을 Intl.DateTimeFormat("Asia/Seoul")로 교체Critical
엑셀 업로드 원자성 부재 —
createMany()를 db.$transaction()으로 래핑하여 부분 INSERT 방지Major
AdminUploadPage requireAdmin() 누락 — 서버 컴포넌트 진입점에
requireAdmin() 추가 (이중 보호)Major
차감 모달 잔액 0 미처리 —
currentBalance <= 0 조기 반환으로 차감 불가 안내Minor
InsufficientBalanceError 문자열 비교 —
instanceof InsufficientBalanceError로 교체, lib/errors.ts 분리Minor
PointType 문자열 리터럴 중복 — Prisma 생성 enum
PointType을 types/index.ts에서 re-export Phase 4 코드 리뷰 → 반영 완료
Critical
admin-logger try-catch 누락 — 디스크 쓰기 실패 시 API 500 방지,
process.stderr.write로 fallbackCritical
rate-limit Map 메모리 누수 —
setInterval().unref()로 5분마다 만료 엔트리 정리Major
getJWTSecret() 4곳 중복 —
lib/jwt.ts로 추출 후 middleware · auth · login · register 공유Major
upload rate limit 순서 오류 —
requireAdmin() 이후로 이동, 관리자 userId 기반 키 사용Major
deduct rate limit 미적용 —
deduct:admin:{userId} 기준 30/분 rate limit 추가Major
seed upsert 비밀번호 덮어쓰기 —
update 절에서 password 제거, create에만 포함Minor
비관리자 리다이렉트 대상 오류 —
/admin/login → /dashboard로 변경Minor
admin 회원목록에 admin 계정 노출 —
where: { role: "user" } 필터 추가Minor
remainingAmount null 단언 사용 —
grant.remainingAmount! → ?? 0으로 교체🔐인증 분리
middleware.ts는
jose로 JWT 검증만. bcrypt는 Edge Runtime 불가 — Node.js Route Handler 전용🛡️이중 권한 검증
Middleware 라우트 보호 + 모든 관리자 API 진입점에서
requireAdmin() 재검증📊포인트 잔여액 산식
SUM(remaining_amount) WHERE type=grant AND expires_at>NOW() AND remaining_amount>0⚡FIFO 차감 트랜잭션
ORDER BY expires_at ASC · Prisma.$transaction Serializable · remainingAmount 감산 + deduct 로그 INSERT
🕐소멸 판별 멱등성
expires_at > NOW() 조건만 사용. GET 요청에서 절대 UPDATE 수행 금지🗓️KST 타임존 처리
Intl.DateTimeFormat("Asia/Seoul")로 expires_at = granted_at + 1년 KST 자동 계산