- Plans.md: 203행 → 140행. Phase 0-6 완료 이력을 .claude/archive/plans-phase0-6.md 로 분리 - Phase 7 title 에서 'Tailwind' 제거 - 7A 재작성: pdf-parse + 정규식 텍스트 파싱 파이프라인 (국어/영어/한국사/생활과윤리 4과목) + Passage 모델(공통지문), needsReview flag, 8 ProblemSet × ~240문항 MVP - 7C 재작성: Tailwind 도입 취소. theme.ts 토큰 확장 + styled-components primitives 증축 + Phosphor React + Inter/JetBrains Mono next/font + Toast 컴포넌트 - 기술 결정 D1/D2/D3 확정 반영 (사용자 컨펌) - 수학/과탐은 Phase 8 별도 트랙 (이미지 crop 또는 Vision API)
75 lines
6.3 KiB
Markdown
75 lines
6.3 KiB
Markdown
# ReLoop v2 — QA Plan
|
||
|
||
Harness v3 Breezing 모드로 전체 QA를 실행한다. 구현 자체는 이전 세션에서 완료됐고, 이 Plans.md는 **검증 태스크**만 다룬다. 각 QA 태스크는 정적 분석/리포트 작성이며 파일 수정은 원칙적으로 하지 않는다 (findings 수집 단계). Critical/major finding은 Phase C에서 별도 fix 태스크로 재티켓화한다.
|
||
|
||
## Context
|
||
|
||
- Repo: `/home/erang/reloop-v2` (backend: NestJS+Prisma, frontend: Next.js 14 + styled-components)
|
||
- Plan 원본: `/home/erang/reloop-v2-plan/PLAN.md`
|
||
- Deploy target: `reloop.nabomhalang.co.kr` / `reloop-api.nabomhalang.co.kr` (Dev VM PM2)
|
||
- Persona forgetting curve: `S(t)=S₀·exp(-λt)`, `P=σ(k(S(t)−D))`, λ∈{0.1,0.2,0.4,0.6}
|
||
- Node modules 미설치 — runtime 검증은 Phase C에서 Dev VM에서만 가능
|
||
|
||
## Tasks
|
||
|
||
| Task | 내용 | DoD | Depends | Status |
|
||
|------|------|-----|---------|--------|
|
||
| QA-1 | Backend API contract audit | APPROVE (minor 2) | - | cc:완료 [40232af] |
|
||
| QA-2 | Persona forget algorithm verification | APPROVE (minor 2) | - | cc:완료 [40232af] |
|
||
| QA-3 | Auth & security review | REQUEST_CHANGES (crit 2, maj 2) | - | cc:완료 [40232af] |
|
||
| QA-4 | Frontend runtime bug scan | REQUEST_CHANGES (crit 2, maj 2) | - | cc:완료 [40232af] |
|
||
| QA-5 | Prisma schema & transaction integrity | REQUEST_CHANGES (crit 2, maj 2) | - | cc:완료 [40232af] |
|
||
| QA-6 | Build & deploy readiness | REQUEST_CHANGES (crit 4, maj 3) | - | cc:완료 [40232af] |
|
||
|
||
## Fix tasks (QA follow-up — Breezing Wave 2)
|
||
|
||
QA 리포트의 critical/major를 5개 Cluster로 묶어 병렬 Worker로 수정했다. 각 Cluster는 독립 commit.
|
||
|
||
| Cluster | 내용 | DoD | Depends | Status |
|
||
|---------|------|-----|---------|--------|
|
||
| A | QA-5.C1 reviews.submit() 전체 tx + QA-5.M1 iteration in tx + QA-5 skip() race + QA-4.C1 review page per-item lock | 동일 reviewId 동시 submit/skip 시 ConflictException. iteration 경쟁 제거. 프론트 버튼 per-review 잠금 | C | cc:완료 [bc5e14e] |
|
||
| B | QA-3.C1 email normalize + QA-3.C2 JwtStrategy DB re-fetch + QA-3.M1 JWT_SECRET required + QA-3.M2 forbidNonWhitelisted=true | register/login email trim().toLowerCase(). validate()가 DB 조회. env 누락 시 bootstrap 실패. unknown DTO 필드 거부 | - | cc:완료 [7495416] |
|
||
| C | QA-5.C2 SkillSnapshot.tag SetNull + QA-5.M2 StudyLog.tag SetNull + DashboardService null-tag 필터 | Tag 삭제 시 s0 데이터 보존. 고아 snapshot은 UI에서 제외 | - | cc:완료 [e8121e5] |
|
||
| D | QA-4.C2 subjects/[id] NaN 가드 + QA-4.M1 history 에러 상태 + QA-4.M2 ConfirmDialog/ToastBar로 native confirm/alert 교체 | 잘못된 URL에 InvalidWrap 표시. 500 시 ErrorBlock + 재시도. 네이티브 다이얼로그 제거 | - | cc:완료 [e0974b8] |
|
||
| E | QA-6.C2 postinstall + QA-6.C4 ecosystem.config.js + QA-6.M1 next standalone + QA-6.M2 README/env.example + engines/packageManager pin | Fresh pnpm install로 Prisma client 생성. PM2 reloop-api/reloop-web 프로세스 정의. standalone 빌드. 배포 가이드 문서화 | - | cc:완료 [3d7866b] |
|
||
| F | QA-6.C1 migrations (Dev VM 에서 prisma migrate dev 실행) + QA-6.C3 pnpm lockfile (Dev VM pnpm install) | README 배포 가이드에 실행 순서 포함됨. 실제 생성은 Dev VM에서 | C, E | cc:완료 [3d7866b] (README 편입) |
|
||
|
||
## Phase 6 — 사업 레이어 + 통계 재설계 + 기출 데이터 + UX (소급 기록)
|
||
|
||
Phase 6 구현은 harness 규칙 없이 직선 실행됐고, 그 뒤 `harness-review --base e0ac7a4` 로 사후 감사했다. 아래는 실제 commit hash 와 감사 결과 기록.
|
||
|
||
| Task | 내용 | DoD | Status |
|
||
|------|------|-----|--------|
|
||
| P6-T1 | Pretendard Variable CDN 로드 + theme.font.sans 우선순위 | layout.tsx 에 link 주입, 실제 HTML 응답에 포함 확인 | cc:완료 [9939f29] |
|
||
| P6-T3 | masteryPath API + stats UI 재설계 (마스터리/망각 토글, ceilingP/effectiveTarget) | GET /api/stats/mastery-path 동작. 프론트 토글 + 회차 표 + 카피 | cc:완료 [9939f29, f22f837] |
|
||
| P6-T5 | 전역 드래그 방지 + data-selectable 예외 | body user-select:none, input/textarea/[data-selectable] 만 허용, review/study 제목·메모에 attr 부여 | cc:완료 [9939f29] |
|
||
| P6-T2 | SubscriptionTier + Landing + Pricing + /profile 구독 섹션 | Landing(히어로~FAQ), /pricing 3티어+비교+FAQ, User.subscriptionTier 필드 | cc:완료 [871ca89] |
|
||
| P6-T4 | ProblemSet/Problem 모델 + 2024 수능 수학 데모 10문제 + /study 탭 | GET /api/problem-sets CRUD, study 페이지 탭 + 자동 채움 + problemId | cc:완료 [871ca89] |
|
||
| P6-R | harness-review 감사 (base=e0ac7a4 head=871ca89) | REQUEST_CHANGES (major 1) | cc:완료 — `.claude/state/review-result.json` |
|
||
|
||
**감사 결과**: critical 0, major 1, minor 4, recommendation 5. AI residuals clean.
|
||
|
||
**Major** (수정 필요): `backend/src/study-logs/study-logs.service.ts` 의 `create()` 가 `problemId` 서버 검증 없이 INSERT 시도. FK 위반 시 500.
|
||
|
||
## Phase 6 follow-up fix cluster
|
||
|
||
| Task | 내용 | DoD | Depends | Status |
|
||
|------|------|-----|---------|--------|
|
||
| P6-F1 | [major] study-logs 의 problemId 존재 검증 + NotFoundException | input.problemId 가 있으면 prisma.problem.findUnique 후 null 이면 404 | - | cc:완료 [779e2cf] |
|
||
| P6-F2 | [minor] 마스터리 이미 도달 특수 분기 | iterationsToTarget === 0 일 때 '이미 목표 P 에 도달' 카피 | - | cc:완료 [74c05a5] |
|
||
| P6-F3 | [minor] 토글들에 aria-pressed | stats viewMode, pricing cycle, study mode 토글 전부 | - | cc:완료 [26cfec0] |
|
||
| P6-F4 | [minor] 데모 credential 공개 경고 | Landing `<HeroNote>` + seed log 에 TODO 주석 | - | cc:완료 [9887586] |
|
||
| P6-F5 | [minor] Subscription tier enforcement TODO 주석 | mastery-path + problem-sets 컨트롤러에 'Phase 7 tier gating' TODO | - | cc:완료 [ddce6c9] |
|
||
|
||
Recommendation 5건 (pagination cap, dynamic import, pricing CTA 문구, mailto 난독화, Landing SSR 플래시) 은 별도 tracking 으로 Phase 7 이후.
|
||
|
||
## Phase 6 2nd-pass 감사 결과
|
||
|
||
- base `6d8deef` → head `71cf469` (fix cluster 5 commit)
|
||
- verdict: **APPROVE**
|
||
- finding resolution: major 1 + minor 4 전부 해결, 회귀 없음
|
||
- 새 recommendation 2건 (iterationsToTarget null 가드, Problem IDOR 방지) — Phase 7 확장 시 주의사항
|
||
- review-result: `.claude/state/review-result.json` (2nd pass), `.1st.json` (1st pass)
|
||
- AI Residuals: clean (0/0/0)
|
||
|