# ReLoop v2 — Plans.md (Phase 7 active)
작업 히스토리 (Phase 0~6 완료) 는 `.claude/archive/plans-phase0-6.md` 로 이동. 이 파일은 **현재 활성 Phase 만** 다룬다.
## Context
- Repo: `/home/erang/reloop-v2` (backend NestJS+Prisma, frontend Next.js 14 + styled-components)
- Deploy: `reloop.nabomhalang.co.kr` / `reloop-api.nabomhalang.co.kr` (mailu-dev VM PM2, HEAD cc946e4)
- DB: MariaDB on 10.10.10.146:33006, `reloop_v2`
- Persona forgetting curve 운영 중, 2nd pass review APPROVE
## Phase 7 — KICE 실데이터 + 사용자 문제집 추가 + 12 페이지 재디자인 (styled-components)
**목적**: 2025/2026학년도 수능 실제 PDF (mailu-dev `/home/dev/reloop-v2/data/kice/`, 290MB) 를 텍스트 파싱해서 ProblemSet 카탈로그로 import 하고, 사용자가 원하는 문제집을 자기 목록에 추가해 개별 문항을 풀 수 있게 한다. 동시에 사용자 제공 디자인 12종을 styled-components 로 재현해 프론트엔드를 재구축한다. 수식이 많은 수학/과탐은 이미지 crop 필요성 때문에 Phase 8 별도 트랙으로 분리.
### 기술 결정 (사용자 확정분)
| # | 결정 포인트 | 확정 내용 |
|---|---|---|
| D1 | CSS 스택 | **styled-components 유지**. Tailwind 도입 안 함. 디자인 파일의 Tailwind 클래스를 styled-components CSS 로 1:1 변환. 디자인 토큰은 기존 `theme.ts` 에 확장 |
| D2 | PDF 파싱 방식 | **pdf-parse + 정규식 기반 본문 파싱**. 문제 본문·선택지·정답을 DB 에 저장해 우리 화면 디자인으로 직접 렌더. PDF Viewer 임베드 안 함 |
| D3 | 과목 범위 (MVP) | **텍스트 과목만**: 국어 / 영어 / 한국사 / 사회탐구(생활과 윤리 1과목). 2025+2026 × 4 = **8 ProblemSet, 약 240 문항**. 수식이 벡터인 수학/과탐은 Phase 8 (별도 이미지 crop 파이프라인) |
| D4 | 정답률 데이터 | MVP 는 `baseCorrectRate: null` 허용. EBSi 오답률 엑셀 수동 import 는 별도 트랙 |
| D5 | 사용자 문제집 모델 | **`UserProblemSet` join** (userId, problemSetId, addedAt, archivedAt?). 카탈로그는 공유, 추가는 개인. ProblemSet 전체를 큐에 bulk insert 하지 않고, 사용자가 개별 Problem 을 "풀기" 할 때 StudyLog 생성 |
### 평가원 PDF 파싱 — 휴리스틱 설계
평가원 기출 PDF 는 포맷이 매우 일관적이라 LLM 없이 정규식 + 룰베이스로 70~90% 파싱 가능. 실패 케이스만 import 스크립트에서 `needs_review=true` flag 로 표시하고 나중에 수동 보정.
**문항 분할 규칙**:
- 문항 시작: `/\n?\s*(\d{1,2})\.\s+/` — 1~45 번
- 선택지 시작: `/[①②③④⑤]/` — 원문자 5 종
- 공통 지문: `/\[(\d+)~(\d+)\]/` — "[1~3]" 형식. 해당 범위 문항은 같은 `Passage` FK 공유
- 정답표 파싱: `/(\d{1,2})번?\s*[①②③④⑤1-5]/` 또는 표 구조 raw text
**Problem 모델 확장**:
- `bodyText Text` — 문제 본문 (선택지 제외)
- `choices Json` — `{"1":"...","2":"...","3":"...","4":"...","5":"..."}`
- `answerNumber Int?` — 1~5
- `passageId Int?` — 공통 지문 FK (별도 `Passage` 테이블)
- `needsReview Boolean @default(false)` — 파싱 엣지케이스 표시
### Phase 7A — KICE 데이터 파이프라인 (텍스트 파싱)
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
| 7A.1 | Prisma 스키마 확장: `ProblemSet` 에 `audioUrls Json?`. `Problem` 에 `bodyText String? @db.Text`, `choices Json?`, `answerNumber Int?`, `passageId Int?`, `needsReview Boolean @default(false)`. 새 모델 `Passage` (공통 지문). examRound 는 기존 examType 과 중복이라 제외 | `prisma migrate dev --name kice_text_fields` 성공, `@prisma/client` 재생성 | - | cc:완료 [e2e3e4b] |
| 7A.2 | `backend/scripts/kice-import.ts` 스캐너 — `data/kice/{year}/{과목}/*.pdf` 순회, `생활과 윤리` 특수 경로 처리, 영어 mp3 수집, dry-run 출력 | `pnpm cli:kice-import --dry-run` 이 2025/2026 전체 파일을 tree 로 출력 | 7A.1 | cc:완료 [35dcf82] |
| 7A.3 | **정답표 파서**: `pdftotext -layout` + 홀/짝 분리 + `/(\d{1,2})\s*[①②③④⑤]/` + 원문자→1..5 정규화. 2025 는 이미지 PDF 라 0 bytes → answerNumber null + needsReview | 2026 4과목 정답 추출 완료, 2025 는 OCR 필요 flag | 7A.2 | cc:완료 [35dcf82] |
| 7A.4 | **문제지 파서 — 본문 분할**: `pdftotext -raw` (2단 컬럼 physical order), `/^\d{1,2}\.\s+/` monotonic 체크, ①..⑤ 위치로 본문/선택지 분할 | 2026 4과목 각 45문항 전부 본문+5 선택지 분리 | 7A.2 | cc:완료 [35dcf82] |
| 7A.5 | **공통 지문 파서**: `/\[\s*(\d+)\s*[~~-]\s*(\d+)\s*\]/` 감지 → Passage 레코드 생성 → 범위 내 Problem 에 passageId FK 부착 | 2026 국어/영어에서 26개 passage 생성, 각 Passage 에 2+문항 연결 | 7A.4 | cc:완료 [35dcf82] |
| 7A.6 | **파싱 실패 가드**: bodyText <10 chars, choices!=5, 빈 choice → needsReview=true | 2026 영어 5 needsReview (듣기 문항), 2025 전체 130 needsReview (OCR 대상) | 7A.4, 7A.5 | cc:완료 [35dcf82] |
| 7A.7 | **ProblemSet + Problem upsert**: (year, examType, subjectName) upsert, Passage delete+recreate, Problem upsert (problemSetId_number), audioUrls 영어만 | 로컬 DB: 9 ProblemSet / 260 Problem / 26 Passage, 재실행 멱등성 확인 | 7A.3, 7A.6 | cc:완료 [35dcf82] |
| 7A.8 | `seed.ts` 의 DEMO_PROBLEMSET(2024 수학) 블록 제거 + 2024 수학 ProblemSet DB DELETE + seed 재실행 검증 | seed.ts DEMO_PROBLEMSET 0건, seed 재실행 에러 없음, ProblemSet.count() 는 kice import 만 | 7A.7 | cc:완료 [35dcf82] |
**제외** (Phase 8 별도 트랙): 수학·과탐·직업탐구·제2외국어 — 수식/도표 비율 높아서 텍스트 파싱 부적합. 이미지 crop 또는 Vision API 파이프라인이 필요해 Phase 7 에 포함하지 않음.
### 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 — 디자인 토큰 확장 + styled-components primitives 증축
기존 `theme.ts` + `primitives.tsx` 를 유지하고 디자인 파일의 토큰/컴포넌트만 이식한다. Tailwind 도입 없음.
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
| 7C.1 | `frontend/src/styles/theme.ts` 확장 — 새 컬러 (bgDeep `#0F0F14`, surfaceDeep `#15151C`, surfaceHover `#1A1A24`, borderSoftAlpha `rgba(255,255,255,0.06)`, brandGradient 함수), shadow (glowIndigo/glowViolet/cardElevated), keyframes(fadeInUp/float/slideToast/shimmer), font.display (Inter) | `theme.color.bgDeep` 등 5+ 신규 토큰 접근 가능, 기존 토큰과 공존 | - | cc:완료 [ba4d6e4] |
| 7C.2 | Phosphor Icons React 도입 — `pnpm add @phosphor-icons/react`. `components/ui/Icon.tsx` 어댑터 (name 매핑 + size/weight props) | `` 가 디자인 파일의 `` 와 시각적으로 동일 | 7C.1 | cc:완료 [0d2303a] |
| 7C.3 | Inter + JetBrains Mono 웹폰트 — `next/font/google` 로 Pretendard(유지) 외에 Inter, JetBrains Mono 로딩. `theme.font.display` / `theme.font.mono` 바인딩, FOUT 없음 | 3종 웹폰트가 production HTML 에 load | 7C.1 | cc:완료 [140a7c3] |
| 7C.4 | `components/ui/primitives.tsx` 증축 — 새 variants: `Button $variant="white"` (흰색 CTA, 디자인 1의 "지금 시작"), `Card $variant="glow"` (radial glow), `KbdHint` (키보드 단축키 pill), `StatCard` (대형 숫자 카드 레이아웃), `AppShellLayout` | 5+ 신규 export, TypeScript 통과 | 7C.1 | cc:완료 [b9874ad] |
| 7C.5 | `components/ui/Toast.tsx` 신규 — slideToast 애니메이션, undo 버튼 + 원형 progress ring (2초 타이머). `showToast({ message, onUndo })` 헬퍼 | toast 호출 시 bottom-center 슬라이드 인, 2초 후 자동 dismiss 또는 undo 클릭 시 콜백 | 7C.1, 7C.2 | cc:완료 [eb10dfe] |
### Phase 7D — 페이지 재디자인 (12종)
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
참조 디자인 파일: `/home/erang/reloop-v2/.claude/archive/designs/design-NN.html` (Tailwind 기반 mockup, styled-components 로 1:1 이식). **Tailwind 도입하지 않음** — 클래스를 styled-components CSS 로 변환.
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
| 7D.1 | **AppShell (사이드바)** — design-01.html 의 `aside.w-64` 레이아웃 (256px, 다크 bgDeep, indigo radial glow blur-80px, backdrop-blur-xl). 로고 indigo→violet gradient + Phosphor `ph-infinity`, nav 5 항목(대시보드/복습/학습 기록/통계/과목 및 태그) Phosphor icons, 활성 라우트 bg-white/5 + border-white/5, 하단 user card (아바타 + nickname + plan badge + caret 드롭다운). 모바일에서는 BottomNav 로 대체 | 기존 SideNav.tsx 교체, `/dashboard` 진입 시 새 사이드바 렌더, 활성 라우트 강조, 로그아웃 클릭 동작 | 7C.5 | cc:완료 [d35fc08] |
| 7D.2 | **Landing (`/`)** — design-02.html 1:1 이식. nav + hero "잊기 직전에 딱 한 번 더" + screenshot mockup + features 3카드 + how-it-works 4단계 + stats 카운터 + pricing teaser + FAQ + footer. SSR/하이드레이션 (로그인 상태면 `/dashboard` 리다이렉트는 middleware 쿠키) | 로그아웃 상태에서 `/` 200, 모든 섹션 렌더, 로그인 사용자는 `/dashboard` 리다이렉트 | 7C.5 | cc:완료 [0605ba8] |
| 7D.3 | **Auth (`/login` + `/register`)** — design-04.html 이식. 단일 카드 UI, 로그인/회원가입 탭 토글, 이메일/비번/닉네임 필드, 에러 shake 애니메이션, "로그인 없이 체험하기" = 데모 계정 자동 로그인 | 두 경로 모두 같은 컴포넌트 사용 (URL path 로 초기 탭 결정), 기존 auth flow 유지 + 에러 UI | 7C.5 | cc:완료 [750378c] |
| 7D.4 | **Pricing (`/pricing`)** — design-03.html 이식. 월/연 토글, Free/Pro(가장 인기)/School 3 카드, "모든 플랜 공통 4 기능", 상세 기능 비교 테이블(3 섹션 구조), FAQ 4개, 바닥 CTA | 토글 동작 시 가격 숫자 전환, 테이블 모바일 숨김, FAQ 아코디언 | 7C.5 | cc:완료 [92e054c] |
| 7D.5 | **Dashboard filled (`/dashboard`)** — design-01.html 우측 main 영역 이식. 헤더 (오늘 N개 복습 대기 + streak 뱃지 `ph-fill ph-fire`), 3 통계 카드 (오늘 큐 / 주간 정답률 + sparkline / 마스터리 원형 SVG 34%), "곧 내 것이 되는 영역" 리스트, "최근 학습" 리스트, 적응형 학습 코치 배너. 실제 API (/dashboard/summary) 연동 | 데모 계정 로그인 후 `/dashboard` 에서 실데이터 렌더, 마스터리 원형 SVG + streak 숫자 정확 | 7D.1 | cc:완료 [eeb772a] |
| 7D.6 | **Dashboard empty (`/dashboard`)** — design-08.html 이식. Welcome + 3단계 체크리스트. 조건: `subjects.length === 0 && studyLogs.length === 0` 에 자동 전환 | 신규 계정 (/register 직후) 은 empty 상태 렌더, 과목 1개 이상 생기면 filled 로 | 7D.5 | cc:완료 [e79c4d8] |
| 7D.7 | **Review 세션 (`/review`)** — design-05.html 이식. 집중 모드 헤더 (뒤로 + ReLoop 로고 + 집중 모드 뱃지), Day N 제목 + 진행률 바 + 키보드 힌트, 카드 스택 (3 layer stacked), 과목 뱃지 + 회차 + 예측 P%, 메모 블록, 4 액션 버튼 (틀림/부분/맞음/스킵) 색상별, Toast "결과 기록됨 + undo 2s" | 2026 큐에서 카드 전체 처리 + undo 동작 + 단축키 1/2/3/S | 7D.1 | cc:완료 [fdc3e4f] |
| 7D.8 | **Study History (`/study/history`)** — design-10.html 5 컬럼 테이블 (항목/과목·태그/날짜·시간/결과/자신감) 이식. 검색 + 과목 필터 + 날짜 필터 + 결과 탭. 페이지네이션 (20건/페이지). design-12.html 는 참고용 대안 | `/api/study-logs` 전체 목록이 테이블 렌더, 필터 동작, 빈 상태 UI | 7D.1 | cc:완료 [46c6a45] |
| 7D.9 | **Subjects & Tags (`/subjects`)** — design-07.html 이식. 상단 tabs (전체/과목별), 과목 섹션 카드 (색상 dot + 이름 + 마스터리 %), 각 섹션 안에 태그 리스트 (P-value + bar + 수정/보관 액션) | /subjects 에서 실제 데이터 렌더, 태그 편집 인라인, 보관 soft archive 동작 | 7D.1 | cc:완료 [0850507] |
| 7D.10 | **Stats (`/stats`)** — design-11.html 이식. 학습 히트맵 (주×월 grid) + 과목별 정답률 bar 리스트 + 마스터리 P-value 성장 곡선 (SVG path). 기존 `/api/stats/*` + `/api/stats/mastery-path` 활용. design-06.html 는 참고용 대안 | 3 패널 전부 실데이터 렌더, 곡선 mastery-path 응답 기반 | 7D.1 | cc:완료 [0e61f0e] |
| 7D.11 | **Settings/Profile (`/profile`)** — design-09.html 이식. 계정 + Pro plan 카드, 학습 목표 슬라이더, 알고리즘 민감도 3 카드, 3 toggle 알림, 연속 학습 달력 (7 열 heatmap), 데이터 export / 진행 초기화 / 로그아웃 | 모든 설정이 `/me/profile` PATCH 와 연동, 변경사항 저장 시 즉시 반영 | 7D.1 | cc:완료 [22f3be2] |
| 7D.12 | **Study 폼 (`/study`)** — 디자인 제외 영역. 기존 기능 유지 + Phase 7B 의 "내 문제집" 탭 추가는 7B 에서. 이 태스크는 기존 탭들을 새 theme/primitives 로 시각적 통일만 | 기존 플로우 유지, 새 theme 컬러/폰트 적용 | 7C.5 | cc:완료 [22ee6ab] |
### Phase 7F — 7D 피드백 리비전 (용어 순화 + 모의고사 + 모바일)
사용자 피드백 3건을 정리한다.
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
| 7F.1 | **수학 용어 순화** — UI 전반에서 `P`, `P-value`, `P=0.85`, `P ≥ 0.9` 같은 수학 표기를 제거하고 "예상 정답률", "숙련도", "마스터 기준(90%)" 같은 자연어로 교체. 변경 대상: dashboard/stats/review/subjects/profile/dashboard-empty 등 모든 UI 텍스트. 내부 변수명/API 필드는 유지, **화면에 표시되는 문자열만** 변경 | 모든 페이지에서 "P=" / "P-value" / "P ≥" 리터럴 0건, 의미 동등한 자연어로 대체 | 7D.12 | cc:완료 [cbc3af2] |
| 7F.2 | **모의고사 학습 플로우** — `/study` 의 "새 학습 기록" 화면에 "문제집에서 풀기" 탭 추가. 탭에서 Phase 7A 로 import 된 KICE ProblemSet 을 선택 → 모의고사 인터페이스 (시험지 느낌의 문제 네비게이션, 카운트다운 타이머, 1-5 선택지 버튼, 표시/건너뛰기, 답안지 확인 모드, 제출 후 각 문항 자동 채점 + StudyLog 일괄 생성). 과목별 실제 시험 시간 매핑 (국어 80분/영어 70분/한국사·탐구 30분) | 데모 계정으로 2026 국어 선택 → 타이머 동작 → 문항 번호 클릭 네비 → 답 체크 → 제출 → 각 Problem 당 StudyLog row 생성 + /dashboard 의 "최근 학습" 에 즉시 반영 | 7A.7, 7D.12 | cc:완료 [31d62fb] |
| 7F.4 | **파서 리팩토링 + segmentation 수정** — `backend/scripts/kice-import.ts` 의 파싱 로직을 `backend/src/problem-sets/parsing/` 모듈로 추출 (범용 설계, 전략 기반). 페이지 헤더/푸터 strip (`제N교시`, `홀수형/짝수형`, `2026학년도 대학수학능력시험 문제지 N`, 페이지 번호, 저작권 문구), 문제 경계 정확도 (다음 `\d{1,2}\.\s+` OR 다음 `[N~M]` OR EOF 중 최소값), passage 헤더 포함 (`[1~3] 다음 글을 읽고…` 를 본문 안에 보존), ⓐⓑⓒⓓⓔ 각주 마커 유지. 재임포트 후 샘플 문항 수동 검증. 향후 업로드 엔드포인트에서 재사용 가능한 순수 함수 시그니처 (buffer 입력) | 2026 국어/영어/한국사/생활과윤리 재임포트 후 needsReview ≤ 5% (듣기 외), 문항 3번 선택지 안에 다른 지문/헤더 오염 0건, passage 첫 줄 "다음 글을 읽고…" 포함 | 7A.7 | cc:완료 [c9935cf] |
| 7F.5 | **OCR fallback (Vision API)** — 7F.4 이후에도 needsReview 로 남는 엣지케이스 문항만 타겟. `backend/scripts/ocr-fallback.ts` 스크립트가 `pdftoppm` 으로 해당 페이지 crop 렌더 → Claude Vision API 로 구조화 JSON 추출 → Problem 레코드 업데이트. 2025 4과목 답안지 (이미지 PDF) 도 이 경로로 처리 | 2026 4과목 needsReview 0건, 2025 4과목 답안지 OCR 결과로 answerNumber 전부 채움 | 7F.4 | cc:완료 [dcf1eb6] |
| 7F.3 | **모바일 반응형 스윕** — 12 개 신규 페이지 전부 모바일 브레이크포인트 (`theme.breakpoint.tablet` 이하) 대응. 사이드바 숨김 + BottomNav 노출, 그리드 스택, 테이블 카드 레이아웃 전환, 폰트 크기 축소, 패딩 축소. 특히 /dashboard 3카드 1열, /pricing 3카드 1열, /study/history 테이블 → 카드 리스트, /stats 3패널 1열, /review 카드 스택 풀스크린, /subjects 태그 리스트 간격 축소, 모의고사 (7F.2) 인터페이스 모바일 터치 최적화 | iPhone SE (375×667) 뷰포트에서 12 페이지 전부 가로 스크롤 없음, 주요 버튼 터치 영역 44px+, 모바일에서 사이드바 숨김 확인 | 7F.2 | cc:완료 [80b3e98] |
### Phase 7G — 사용자 피드백 리비전
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
| 7G.1 | **IA 재구성 + 모바일 로그아웃** — `/exams` 신규 top-level 라우트로 문제집 카탈로그 분리. SideNav 에 "문제집" 항목 추가, `/study` 폼에서 "문제집에서 풀기" 탭 제거. 시험 페이지 우상단 "집중 모드" 토글 버튼 (ON=풀스크린 / OFF=AppShell 포함). `/login` `/register` 상단 "← 홈으로" 링크. 모바일에서 로그아웃/프로필 접근 (BottomNav 에 프로필 탭 또는 상단 메뉴) | /exams 진입 가능, study 폼 문제집 탭 제거, 집중모드 토글 동작, 로그인에서 홈 이동, 모바일 로그아웃 접근 가능 | 7F.5 | cc:완료 [47a70f2] |
| 7G.2 | **시험 UX 전면 개선** — 네비 4버튼 (이전/건너뛰기/다음/항목이동) 전부 노출, 시험 종료는 `⋮` 메뉴로. 데스크톱/태블릿 문제 패널 드래그 리사이즈 (localStorage). 모바일 탭 시 확대 모달. 자동 채점 제출 시 Subject+Tag 자동 생성/매칭 보장. Problem.imageUrl 렌더 (7H.1 의존). Subject+Tag 자동 생성 강화: topic 필드 → Tag | 4 버튼 동작, 시험 종료 숨김, 데스크톱 리사이즈, 모바일 확대, 제출 후 과목/태그 자동 생성 확인 | 7G.1 | cc:완료 [7c5cdf4] |
| 7G.3 | **StudyLog 상세 + 복습 파이프라인 검증** — `/study-logs/[id]` 신규 상세 라우트 (문항 재열람: 본문/선택지/정답/내 답/메모). 대시보드 "최근 학습" + 학습 기록 행 클릭 → 상세 이동. 모의고사 제출 → ReviewSchedule 생성 플로우 검증 + 오늘 복습 큐 진입 확인. 첫 시도 S₀ 시드 조정 (baseCorrectRate 반영) | 문항 상세 재열람, 모의고사 제출 후 24h 내 복습 큐 등장, 초기 복습 간격 난이도 반영 | 7G.2 | cc:완료 [9068fcb] |
| 7G.4 | **랜딩 리파인 + D-day 동기화 + 애니메이션** — Hero typing effect (3-5개 격려 문구 rotation), 수능 D-day 실시간 계산 (고정 상수 = 2026-11-13, 매일 자동 갱신), 전역 소프트 애니메이션 (카드 hover/scroll fade-in via Intersection Observer), dashboard/review/stats 문구 동기부여 톤 재작성 | Typing effect 동작, D-day 매일 자동 갱신, 스크롤 애니메이션 부드러움, 톤 개선 | 7C.5 | cc:완료 [feb1a62] |
| 7G.5 | **통계 히트맵 리사이즈 + 테마드 폼 컴포넌트 + 과목 페이지 수정** — 통계 히트맵 50% 축소 + 요일 라벨 + 클릭 시 해당일 드로어. `components/ui/Select.tsx` + `DatePicker.tsx` (다크 테마). /study/history 필터/profile 셀렉터/subjects 정렬 전부 새 컴포넌트로 교체. 과목 페이지 모바일 탭 버튼 세로 글씨 수정 → horizontal scroll pill | 히트맵 축소 + 기능, 모든 드롭다운 다크 테마, 과목 탭 가로 정렬 | 7D.10 | cc:완료 [e332d27] |
| 7G.6 | **프로필 + 아바타 + 온보딩** — Backend: `User.avatarUrl`, `POST /me/avatar` multipart (`backend/uploads/avatars/`). Frontend `/profile` 아바타 업로드 UI. `/onboarding` 플로우 보강 (현재/목표 등급, 중점 과목, 수능 연도, 페르소나). 신규 가입 후 자동 진입. 프로필 드롭다운 모바일 접근 | 아바타 업로드 + 저장 + URL 반환, 온보딩 완전 동작, 프로필 드롭다운 모바일 접근 가능 | 7G.1 | cc:완료 [a5dab35] |
### Phase 7H — 수학 + 이미지 기반 문제 지원
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
| 7H.1 | **수학 데이터 + 스키마 + 이미지 파이프라인** — mailu-dev 에서 2025/2026 수학 PDF rsync. Prisma 확장 `Problem.imageUrl String?`, `Problem.pageImageUrl String?` + migration. `parsing/strategies/math.ts` — pdftoppm 으로 per-problem PNG 렌더. 이미지는 `backend/uploads/problems/{year}/{subject}/{number}.png`. `kice-import --subject=수학` 동작, 답안지 OCR fallback 재사용 | 2025/2026 수학 각 30문제 import 완료, imageUrl 채움, 답안 자동 채점 | 7F.4, 7F.5 | cc:완료 [acc35e9] |
| 7H.2 | **프론트 이미지 렌더 + 시험 UI 통합** — Problem 타입에 imageUrl 추가. 시험/복습 UI: imageUrl 있으면 `` 메인. 사진/표 포함 문제도 동일 경로. /uploads static 서빙 NestJS | 수학 문항 시험 화면에서 정상 렌더, 이미지 포함 문제 시각 표시 | 7H.1, 7G.2 | cc:완료 [8a91ab4] |
### Phase 8 — 판매 준비 (production ready)
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
| 8.1 | **PWA + SEO 메타** — manifest.webmanifest, 앱 아이콘 세트 (192/512 + apple-touch), theme-color, per-route `generateMetadata` 로 OG 태그, robots.txt, sitemap.xml | `/` 설치 배너, OG 미리보기, robots/sitemap 서빙 | 7G.6 | cc:완료 [7ba7b4a] |
| 8.2 | **법적 페이지 + 404/500 + 에러 바운더리** — `/privacy`, `/terms`, `/contact` 페이지 (footer 링크). `app/not-found.tsx`, `app/error.tsx`, `app/global-error.tsx` 테마드 페이지 | 4 경로 렌더, 404/500 테마드 | 7G.4 | cc:완료 [eb0e07b] |
| 8.3 | **보안 + rate limit + 환경 하드닝** — helmet 강화, CORS 화이트리스트, @nestjs/throttler 전역 rate limit (분당 60), 데모 계정 평문 비밀번호 stdout 제거, `.env` 템플릿 정리 | 로그인 스팸 60/분 초과 429, seed 평문 노출 0 | 7G.6 | cc:완료 [c36978c] |
### Phase 7I — 2차 피드백 리비전 (이미지 우선 + UX)
| Task | 내용 | DoD | Depends | Status |
|------|------|-----|---------|--------|
| 7I.1 | **전 과목 per-problem 이미지 crop** — `pdftotext -bbox` 로 문제 번호 Y좌표 추출 → `pdftoppm -x -y -W -H` 으로 문제별 PNG crop → `uploads/problems/{year}/{subject}/{number}.png` 저장. 모든 과목 적용. 텍스트 파싱은 보조, 이미지가 주 표시. 재임포트 후 320 문항 전부 imageUrl 보유 | 2025/2026 × 5과목 재임포트, 320 problems 전부 imageUrl, 수학 포함 정상 | 7H.1 | cc:완료 [e76e14e] |
| 7I.2 | **chosenAnswer + 개별 풀기 + 프로필 통합** — (a) `StudyLog.chosenAnswer Int?` migration. 제출 시 저장. (b) `/solve/[studyLogId]` 개별 문제 풀기 라우트. (c) `/profile` + `/settings` 통합: 단일 페이지 탭 | chosenAnswer 저장 + detail 표시, 개별 풀기, 프로필 단일 페이지 | 7I.1 | cc:완료 [4c61b87] |
| 7I.4 | **3차 피드백 일괄** — (a) 집중모드 기본 OFF (review+exam localStorage 기본값 false). (b) /profile 등급 슬라이더 네이티브 HTML → 테마드 커스텀 슬라이더. (c) 문제 표시 이미지 ONLY (텍스트 파싱 제거, 선택지도 이미지). 수학 등 OMR 스타일 1-5 버블 (식 표기 대신). (d) 이미지 가운데 정렬. (e) 국어 지문+문제 세트: passage 이미지 crop 도 생성 + 시험 UI 에서 지문 이미지 먼저 표시. (f) /pricing 연결 (로그인 후 접근). (g) 영어 듣기 오디오 재생기 (mp3 서빙 + `