Files
reloop-v2/.claude/archive/plans-phase0-6.md
reloop 96b2be58e1 plan(phase7): archive 0-6 history + revise 7A/7C for text parsing + styled-components
- 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)
2026-04-12 03:01:04 +09:00

75 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)