plan(phase7): KICE import + UserProblemSet + Tailwind + 12 page redesign
harness-plan create 로 Phase 7 전체 스코프 분해: - 7A (7 tasks): KICE PDF 파이프라인 — schema 확장, 정답표 파싱, /static/kice/ 서빙 - 7B (5 tasks): UserProblemSet — join 테이블, me/problem-sets API, /study 내 문제집 탭 - 7C (5 tasks): Tailwind 도입 — 디자인 토큰 이식, Phosphor React, 새 primitives - 7D (12 tasks): 12 페이지 재디자인 (AppShell, Landing, Auth, Pricing, Dashboard filled/empty, Review, Study History, Subjects, Stats, Profile, Study 폼) - 7E (3 tasks): 배포 + harness-review D1~D5 기술 결정 포인트 5건 사용자 컨펌 대기 상태로 표시.
This commit is contained in:
101
Plans.md
101
Plans.md
@@ -72,6 +72,107 @@ Recommendation 5건 (pagination cap, dynamic import, pricing CTA 문구, mailto
|
|||||||
- review-result: `.claude/state/review-result.json` (2nd pass), `.1st.json` (1st pass)
|
- review-result: `.claude/state/review-result.json` (2nd pass), `.1st.json` (1st pass)
|
||||||
- AI Residuals: clean (0/0/0)
|
- AI Residuals: clean (0/0/0)
|
||||||
|
|
||||||
|
## Phase 7 — KICE 실데이터 + 사용자 문제집 추가 + Tailwind + 12 페이지 재디자인
|
||||||
|
|
||||||
|
**목적**: 2025/2026학년도 수능 실제 PDF (mailu-dev `/home/dev/reloop-v2/data/kice/`, 290MB, 46+과목 × 2연도) 를 ProblemSet 카탈로그로 import 하고, 사용자가 원하는 문제집을 자기 학습 목록에 추가해서 복습 큐로 푸는 플로우를 만든다. 동시에 사용자 제공 디자인 12종을 반영해 Next.js 프론트엔드를 Tailwind CSS 로 재구축한다.
|
||||||
|
|
||||||
|
### 기술 결정 (확정 필요)
|
||||||
|
|
||||||
|
| # | 결정 포인트 | 제안 | 대안 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| D1 | CSS 스택 | **Tailwind CSS 3.4 신규 도입 + 기존 styled-components 와 공존 (hybrid)**. 새 페이지는 Tailwind, 기존 페이지는 점진 제거 | styled-components 로 디자인 재현 (공수 2~3배) |
|
||||||
|
| D2 | PDF 파싱 깊이 | **정답표만 파싱** (pdf-parse + 번호→정답 표 구조). 문제지는 파일 URL 로만 저장 (이미지/수식 포함이라 본문 추출 불가) | 문제지도 OCR (Phase 8+) |
|
||||||
|
| D3 | 문제집 범위 (MVP) | **2025 + 2026학년도 수능 × 주요 5영역** (국어/수학/영어/한국사/통합사탐 1과목 = 생활과 윤리). 합계 약 10 ProblemSet. 탐구 전 과목은 Phase 7 후반에 확장 | 46과목 × 2연도 = 92 ProblemSet 을 한 번에 |
|
||||||
|
| D4 | 정답률 데이터 소스 | 우선은 `baseCorrectRate: null`. 향후 EBSi 등급컷/오답률 엑셀 수동 import 로 보강 (별도 트랙) | 데모 대략치 (이미 P6-F4 에서 제거 대상) |
|
||||||
|
| D5 | 사용자 문제집 모델 | **UserProblemSet join (userId, problemSetId, addedAt, archivedAt?)** — 카탈로그는 공유, 선택은 개인. 추가 시 StudyLog 는 만들지 않고 "내 목록" 에만 표시. 실제 복습 큐 진입은 개별 Problem 을 /study 에서 "풀기" 할 때 | ProblemSet 전체를 한 번에 복습 큐 bulk insert |
|
||||||
|
|
||||||
|
### Phase 7A — KICE 데이터 파이프라인
|
||||||
|
|
||||||
|
| Task | 내용 | DoD | Depends | Status |
|
||||||
|
|------|------|-----|---------|--------|
|
||||||
|
| 7A.1 | Prisma 스키마 확장: `ProblemSet` 에 `pdfProblemUrl`, `pdfAnswerUrl`, `audioUrls Json?` (영어 듣기), `examRound` (sat/mock-jun/mock-sep). `Problem` 에 `correctChoice Int?`, `sourcePage Int?` | `prisma migrate dev --name kice_fields` 성공, `prisma generate` 통과 | - | cc:TODO |
|
||||||
|
| 7A.2 | backend에 `scripts/kice-import.ts` 생성: `data/kice/{year}/{과목}/**/*.pdf` 를 스캔 → 파일 네이밍 룰로 `(year, 과목, 형태)` 추출 | dry-run 모드에서 2025/2026 전체 46+46 과목 리스트 + 각 PDF 경로를 콘솔 출력 | 7A.1 | cc:TODO |
|
||||||
|
| 7A.3 | 정답표 PDF 파싱: `pdf-parse` 로 텍스트 추출 후 "1번 ⑤ 2번 ② ..." 정규식 파싱 → `{ number, correctChoice }` 배열 | 2026 국어·수학·영어·한국사 정답표 4개에서 각각 30~45문항이 정확히 뽑힘 (수동 검증 3문항 스팟체크) | 7A.2 | cc:TODO |
|
||||||
|
| 7A.4 | import 로직: `ProblemSet` upsert (year, examType='sat', subjectName) + 파싱된 문항으로 `Problem` upsert. `pdfProblemUrl`/`pdfAnswerUrl` 은 `/static/kice/...` 정적 경로로 저장 | 2026학년도 5개 과목 (국/수/영/한국사/생활과윤리) 이 DB 에 정상 upsert, 멱등성 확보 | 7A.3 | cc:TODO |
|
||||||
|
| 7A.5 | NestJS main.ts 에 `app.useStaticAssets(join(__dirname, '..', '..', 'data', 'kice'), { prefix: '/static/kice/' })` 추가하여 PDF 를 HTTP 로 서빙. 단 **read-only, 인증 가드 적용** (PDF 는 저작권 자료) | `GET /static/kice/2026/국어/(26)국어영역_문제지.pdf` 가 JwtAuthGuard 통과 후 200, 미인증 401 | 7A.4 | cc:TODO |
|
||||||
|
| 7A.6 | `pnpm cli:kice-import` 명령어 등록, mailu-dev 에서 실행 → DB에 2025+2026 × 5개 과목 = 10 ProblemSet, 약 300 문항 upsert | mailu-dev 의 reloop_v2 DB 에 `ProblemSet.count() = 10`, `Problem.count() >= 250` | 7A.5 | cc:TODO |
|
||||||
|
| 7A.7 | 기존 2024 수능 수학 데모 seed 10 문제 제거 (`seed.ts` 에서 DEMO_PROBLEMSET 블록 삭제) | `seed.ts` 에 DEMO_PROBLEMSET 참조 0건. seed 재실행 시 문제집 중복 생성 안 됨 | 7A.6 | cc:TODO |
|
||||||
|
|
||||||
|
### Phase 7B — 사용자 문제집 추가 플로우
|
||||||
|
|
||||||
|
| Task | 내용 | DoD | Depends | Status |
|
||||||
|
|------|------|-----|---------|--------|
|
||||||
|
| 7B.1 | Prisma: `UserProblemSet` 모델 (`userId`, `problemSetId`, `addedAt`, `archivedAt DateTime?`, `@@unique([userId, problemSetId])`, `@@index([userId])`) | migration 통과, User/ProblemSet 양방향 relation 정의됨 | 7A.1 | cc:TODO |
|
||||||
|
| 7B.2 | backend `me/problem-sets` module: `GET /api/me/problem-sets` (내 추가분 list), `POST /api/me/problem-sets` (`{ problemSetId }`), `DELETE /api/me/problem-sets/:id` (soft archive) | 데모 계정으로 3개 API 모두 200/201/204 응답. 중복 추가 시 409 | 7B.1 | cc:TODO |
|
||||||
|
| 7B.3 | frontend `lib/api.ts` 에 `UserProblemSet`, `addProblemSet`, `listMyProblemSets`, `removeProblemSet` 타입/헬퍼 추가 | 타입 에러 없이 컴파일, stats 페이지에서 호출 가능 | 7B.2 | cc:TODO |
|
||||||
|
| 7B.4 | frontend `/study` 페이지 탭 재설계: [내 문제집 / 카탈로그 / 직접 입력] 3 탭. "카탈로그" 에 전체 ProblemSet 목록 + 각 카드에 "+ 내 목록에 추가" 버튼. "내 문제집" 에 추가한 것만 표시 | 카탈로그에서 카드 클릭 → 내 목록 탭에 즉시 반영. 중복 클릭 방지 | 7B.3 | cc:TODO |
|
||||||
|
| 7B.5 | "내 문제집" 탭 상세: 선택한 ProblemSet → 문항 리스트 → 각 문항 "풀기" 버튼 → 기존 `/study/form?problemId=N` 으로 이동해 결과 입력 | 데모 계정에서 2026 수학 → 15번 클릭 → 결과 기록 → 복습 큐 진입까지 전 플로우 통과 | 7B.4 | cc:TODO |
|
||||||
|
|
||||||
|
### Phase 7C — Tailwind CSS 도입 + 디자인 토큰
|
||||||
|
|
||||||
|
| Task | 내용 | DoD | Depends | Status |
|
||||||
|
|------|------|-----|---------|--------|
|
||||||
|
| 7C.1 | `pnpm add -D tailwindcss@3.4 postcss autoprefixer`, `npx tailwindcss init -p`, Next.js app router 용 `src/app/globals.css` 에 `@tailwind` 디렉티브 추가 | `pnpm build` 시 Tailwind CSS 가 번들에 포함되고 styled-components 와 공존 | - | cc:TODO |
|
||||||
|
| 7C.2 | `tailwind.config.ts` 에 디자인 토큰 정의: colors(bg/surface/surfaceHover/border/brand indigo·violet/accent), fontFamily(sans=Pretendard, display=Inter, mono=JetBrains Mono), backgroundImage(gradient-radial), keyframes/animation(fadeInUp/float/slideToast) — 디자인 파일의 모든 config 를 1:1 이식 | 디자인 파일의 className 을 그대로 붙여넣어도 스타일이 렌더됨 (단일 페이지 sanity 체크) | 7C.1 | cc:TODO |
|
||||||
|
| 7C.3 | Phosphor Icons 설치 (`pnpm add @phosphor-icons/react`). 기존 layout.tsx 의 CDN `<script>` 제거, React 컴포넌트 import 로 전환 | `<i className="ph ph-squares-four" />` 사용처 전부 `<SquaresFour />` 로 교체 가능 (예시 1개 sanity) | 7C.1 | cc:TODO |
|
||||||
|
| 7C.4 | Inter + JetBrains Mono webfont: layout.tsx 에 `<link rel="preconnect">` + Google Fonts CSS, 또는 `next/font/google` 로 local 로드 | Pretendard + Inter + JetBrains Mono 3종 모두 HTML 응답에 로드 확인 | 7C.1 | cc:TODO |
|
||||||
|
| 7C.5 | `components/ui/tw/` 디렉토리 생성 — 새 Tailwind 기반 primitives (Button, Card, Badge, Input, Select, Toast, ConfirmDialog). 기존 `components/ui/primitives.tsx` 는 마이그레이션 기간 동안 유지 | 새 primitives 5종 이상 export, TypeScript 통과 | 7C.2, 7C.3 | cc:TODO |
|
||||||
|
|
||||||
|
### Phase 7D — 페이지 재디자인 (12종)
|
||||||
|
|
||||||
|
| Task | 내용 | DoD | Depends | Status |
|
||||||
|
|------|------|-----|---------|--------|
|
||||||
|
| 7D.1 | **AppShell (사이드바)** — 디자인 1(Dashboard) 의 `aside.w-64` 레이아웃. 로고 gradient, nav 5개, user plan badge, indigo glow radial. 모바일 반응형 (BottomNav 유지) | `/dashboard` 진입 시 새 사이드바 렌더, 활성 라우트 강조, 로그아웃 드롭다운 동작 | 7C.5 | cc:TODO |
|
||||||
|
| 7D.2 | **Landing (`/`)** — 디자인 2의 nav + hero + screenshot mockup + features 3카드 + how-it-works 4단계 + stats 카운터 + pricing teaser + FAQ + footer. SSR/하이드레이션 fix (hasToken 체크를 middleware 쿠키로) | 로그아웃 상태에서 `/` 200, 모든 섹션 스크롤 애니메이션 동작, 로그인 사용자는 `/dashboard` 리다이렉트 | 7C.5 | cc:TODO |
|
||||||
|
| 7D.3 | **Auth (`/login` + `/register`)** — 디자인 4의 단일 탭 카드 UI. 로그인/회원가입 토글, 이메일/비번/닉네임 필드, 에러 shake 애니메이션, "로그인 없이 체험하기" = 데모 계정 자동 로그인 | 두 경로 모두 같은 컴포넌트 사용 (URL path 로 초기 탭 결정). 기존 flow 유지 + 에러 UI | 7C.5 | cc:TODO |
|
||||||
|
| 7D.4 | **Pricing (`/pricing`)** — 디자인 3의 월/연 토글, Free/Pro(가장 인기)/School 3 카드, "모든 플랜 공통 4 기능", 상세 기능 비교 테이블(3 섹션 구조), FAQ 4개, 바닥 CTA | 토글 동작 시 가격 숫자 애니메이션, 테이블 모바일 숨김, FAQ 아코디언 | 7C.5 | cc:TODO |
|
||||||
|
| 7D.5 | **Dashboard filled (`/dashboard`)** — 디자인 1의 헤더 (오늘 N개 복습 대기) + streak 배지, 3 통계 카드 (큐/주간 정답률/마스터리 원형), 곧 내 것이 되는 영역 카드, 최근 학습 리스트, 적응형 학습 코치 배너. 실제 API (/dashboard/summary) 연동 | 데모 계정 로그인 후 `/dashboard` 에서 실데이터 렌더, 마스터리 원형 SVG + streak 숫자 정확 | 7D.1 | cc:TODO |
|
||||||
|
| 7D.6 | **Dashboard empty (`/dashboard`)** — 디자인 8의 Welcome + 3단계 체크리스트. 조건: `subjects.length === 0 && studyLogs.length === 0` 에 자동 전환 | 신규 계정 (/register 직후) 은 empty 상태 렌더, 과목 1개 이상 생기면 filled 로 | 7D.5 | cc:TODO |
|
||||||
|
| 7D.7 | **Review 세션 (`/review`)** — 디자인 5의 집중 모드 헤더 (뒤로 + ReLoop 로고 + 집중 모드 뱃지), Day N 제목 + 진행률 바 + 키보드 힌트, 카드 스택 (3 layer stacked), 과목 뱃지 + 회차 + 예측 P%, 메모 블록, 4 액션 버튼 (틀림/부분/맞음/스킵) 색상별, slide-toast "결과 기록됨 + undo 2s" | 2026 수학 문제 15개 큐에서 전부 처리 + undo 동작 + 단축키 1/2/3/S 전부 동작 | 7D.1 | cc:TODO |
|
||||||
|
| 7D.8 | **Study History (`/study/history`)** — 디자인 11의 5 컬럼 테이블 (항목/과목·태그/날짜·시간/결과/자신감). 검색 + 과목 필터 + 날짜 필터 + 결과 탭. 페이지네이션 (20건/페이지) | `/api/study-logs` 전체 목록이 테이블 렌더, 필터 동작, 빈 상태 UI | 7D.1 | cc:TODO |
|
||||||
|
| 7D.9 | **Subjects & Tags (`/subjects`)** — 디자인 7의 상단 tabs (전체/과목별), 과목 섹션 카드 (색상 dot + 이름 + 마스터리 %), 각 섹션 안에 태그 리스트 (P-value + bar + 수정/보관 액션) | /subjects 에서 실제 데이터 렌더, 태그 편집 인라인, 보관 soft archive 동작 | 7D.1 | cc:TODO |
|
||||||
|
| 7D.10 | **Stats (`/stats`)** — 디자인 9 의 히트맵 (주×월 grid) + 과목별 정답률 bar 리스트 + 마스터리 P-value 성장 곡선 (SVG path). 기존 `/api/stats/*` + `/api/stats/mastery-path` 활용. 디자인 6 의 bar chart 는 옵션 | 3 패널 전부 실데이터 렌더, 곡선 mastery-path 응답 기반, 히트맵 클릭 시 해당 일 세부 표시 | 7D.1 | cc:TODO |
|
||||||
|
| 7D.11 | **Settings/Profile (`/profile`)** — 디자인 9 의 계정 + Pro plan 카드, 학습 목표 슬라이더, 알고리즘 민감도 3 카드, 3 toggle 알림, 연속 학습 달력 (7 열 heatmap), 데이터 export / 진행 초기화 / 로그아웃 | 모든 설정이 `/me/profile` PATCH 와 연동, 변경사항 저장 시 즉시 반영 | 7D.1 | cc:TODO |
|
||||||
|
| 7D.12 | **Study 폼 (`/study`)** — 디자인 제외 영역. 기존 기능(직접 입력 탭 + 문제집 탭) 유지 + Phase 7B 의 "내 문제집" 탭 추가. Tailwind 클래스로 재작성 | 3 탭 전환 동작, 자동 채움 플로우 유지, tailwind + primitives.tw 사용 | 7B.4, 7C.5 | cc:TODO |
|
||||||
|
|
||||||
|
### Phase 7E — 감사 + 배포
|
||||||
|
|
||||||
|
| Task | 내용 | DoD | Depends | Status |
|
||||||
|
|------|------|-----|---------|--------|
|
||||||
|
| 7E.1 | Backend build + mailu-dev prisma migrate deploy + reloop-api reload | dist/main.js 실행, `/api/me/problem-sets` 3 엔드포인트 200 응답, 2026 수능 5 ProblemSet 노출 | 7A.6, 7B.2 | cc:TODO |
|
||||||
|
| 7E.2 | Frontend build + standalone copy + reloop-web reload | `/`, `/login`, `/pricing`, `/dashboard`, `/review`, `/study`, `/study/history`, `/stats`, `/subjects`, `/profile` 10 경로 전부 200 + 신규 디자인 렌더 | 7D.12 | cc:TODO |
|
||||||
|
| 7E.3 | harness-review code (`--base` = Phase 7 시작 지점) — 30+ commit 대상 감사 | critical/major 0 또는 수정 루프 통과 | 7E.1, 7E.2 | cc:TODO |
|
||||||
|
|
||||||
|
### 범위 / 비 범위
|
||||||
|
|
||||||
|
**포함**:
|
||||||
|
- KICE 2025/2026 × 5 과목 = 10 ProblemSet import
|
||||||
|
- UserProblemSet 추가/조회/보관 API + UI
|
||||||
|
- Tailwind 도입 + 디자인 토큰 이식
|
||||||
|
- 12 페이지 재디자인 (AppShell 포함)
|
||||||
|
|
||||||
|
**제외** (Phase 8 이후):
|
||||||
|
- 2025/2026 탐구 전 과목 (46+46) 전체 import — 먼저 5과목 패턴 확립 후 Phase 8 자동 확장
|
||||||
|
- 정답률/등급컷 데이터 수동 입력 — EBSi 엑셀 별도 트랙
|
||||||
|
- 실제 결제 연동 (PortOne/Toss)
|
||||||
|
- Pro tier 실제 gating 강제 (P6-F5 TODO 주석 유지)
|
||||||
|
- 문제지 PDF 이미지/수식 OCR
|
||||||
|
- PWA manifest / offline 지원
|
||||||
|
- 학교 Pro 전용 기능 (선생님 대시보드)
|
||||||
|
|
||||||
|
### 실행 모드 제안
|
||||||
|
|
||||||
|
전체 35+ 태스크 → **Breezing** 자동 선택되지만 Phase 별로 나눠 순차 실행:
|
||||||
|
1. 먼저 **Phase 7C** (Tailwind 도입) — 5 태스크, 다른 Phase 의 전제
|
||||||
|
2. **Phase 7A** (KICE 파이프라인) — 7 태스크, backend 중심
|
||||||
|
3. **Phase 7B** (UserProblemSet) — 5 태스크, backend + 최소 frontend
|
||||||
|
4. **Phase 7D** (12 페이지 재디자인) — 12 태스크, 가장 무거움. 서브 Phase 로 더 쪼개 가능
|
||||||
|
5. **Phase 7E** (배포+감사) — 3 태스크
|
||||||
|
|
||||||
|
**사용자 컨펌 대기 항목**:
|
||||||
|
- D1 ~ D5 기술 결정 승인
|
||||||
|
- Phase 7D 재디자인을 하나의 세션에서 12개 전부 할지, 서브 Phase (7D-a 사이드바+dashboard / 7D-b auth+landing+pricing / 7D-c review+study+history / 7D-d subjects+stats+profile) 로 쪼갤지
|
||||||
|
|
||||||
## Execution
|
## Execution
|
||||||
|
|
||||||
Mode: **Breezing (명시 플래그 --breezing) — 2 Wave**
|
Mode: **Breezing (명시 플래그 --breezing) — 2 Wave**
|
||||||
|
|||||||
Reference in New Issue
Block a user