- harness-review 2nd pass (base6d8deef→ head71cf469): APPROVE - 1st pass 의 major 1 + minor 4 전부 해결 검증 - 회귀 없음, AI residuals clean - 새 recommendation 2건 (iterationsToTarget null 가드, Problem IDOR) 은 Phase 7 확장 시 주의사항으로 tracking .claude/state/review-result.json: 2nd pass 결과 .claude/state/review-result.1st.json: 1st pass 결과 (히스토리)
112 lines
5.5 KiB
JSON
112 lines
5.5 KiB
JSON
{
|
|
"schema_version": "review-result.v1",
|
|
"verdict": "REQUEST_CHANGES",
|
|
"reviewer_profile": "static",
|
|
"base_ref": "e0ac7a4",
|
|
"head_ref": "871ca89",
|
|
"scope": "phase-6",
|
|
"commits": [
|
|
"9939f29 feat(phase6): mastery-path API + stats UI 재설계 + Pretendard + 드래그 방지",
|
|
"f22f837 feat(stats): mastery-path ceilingP + effectiveTarget",
|
|
"871ca89 feat(phase6): BM tiers + Landing + Pricing + 평가원 기출 ProblemSet"
|
|
],
|
|
"stats": {
|
|
"files_changed": 27,
|
|
"insertions": 2398,
|
|
"deletions": 115
|
|
},
|
|
"ai_residuals": {
|
|
"tool": "review-ai-residuals",
|
|
"scan_mode": "diff",
|
|
"summary": { "verdict": "APPROVE", "major": 0, "minor": 0, "recommendation": 0, "total": 0 }
|
|
},
|
|
"critical_issues": [],
|
|
"major_issues": [
|
|
{
|
|
"id": "P6.major.1",
|
|
"category": "Security",
|
|
"location": "backend/src/study-logs/study-logs.service.ts:64-79",
|
|
"issue": "problemId 가 서버에서 존재 검증 없이 INSERT 된다. 존재하지 않는 problemId 를 넘기면 Prisma FK 위반으로 500 이 튀어 raw stack trace 가 노출될 수 있고, 500 vs 400 응답 차이로 problemId 존재 여부가 leak 된다.",
|
|
"suggestion": "tagId 와 동일한 패턴으로 사전 조회 후 없으면 NotFoundException. subjectId/tagId 체크 블록 바로 뒤에 삽입."
|
|
}
|
|
],
|
|
"observations": [
|
|
{
|
|
"severity": "minor",
|
|
"category": "Security",
|
|
"location": "backend/src/problem-sets/problem-sets.controller.ts, backend/src/stats/stats.controller.ts:58",
|
|
"issue": "subscriptionTier 가 저장만 되고 어디서도 enforce 안 됨. Landing/Pricing 이 mastery-path/problem-sets 를 Pro 전용으로 광고하지만 실제 gating 은 없음.",
|
|
"suggestion": "Phase 7 결제 연동 시 TierGuard 또는 inline 체크. 현재는 TODO 주석 필수."
|
|
},
|
|
{
|
|
"severity": "minor",
|
|
"category": "Quality",
|
|
"location": "frontend/src/app/stats/page.tsx:256-262",
|
|
"issue": "masteryPath 가 이미 목표 도달 상태일 때 iterationsToTarget=0, daysToTarget=0 으로 반환하고 프론트 카피가 '앞으로 0회차 더 복습하면 약 0일 뒤 마스터' 같은 어색한 문구를 낸다.",
|
|
"suggestion": "iterationsToTarget === 0 특수 분기 추가: '이미 목표 P 에 도달한 상태야 ✨'."
|
|
},
|
|
{
|
|
"severity": "minor",
|
|
"category": "Accessibility",
|
|
"location": "frontend/src/app/stats/page.tsx:142-156, frontend/src/app/pricing/page.tsx:37-43, frontend/src/app/study/page.tsx",
|
|
"issue": "마스터리/망각 토글, 월/연 토글, study 탭에 aria-pressed 또는 role=tab/aria-selected 가 없음. 스크린리더가 상태 전달 못함. WCAG 2.1 SC 4.1.2.",
|
|
"suggestion": "각 토글 버튼에 aria-pressed={$active} 추가."
|
|
},
|
|
{
|
|
"severity": "minor",
|
|
"category": "AI Residuals",
|
|
"location": "frontend/src/app/page.tsx:58, backend/prisma/seed.ts:105",
|
|
"issue": "Landing 에 demo@reloop.local / demo1234 가 공개. seed log 에도 평문 비밀번호 stdout 출력. 데모 단계엔 괜찮지만 런칭 시 제거 대상.",
|
|
"suggestion": "TODO 주석 추가. Phase 7 런칭 전 제거 또는 /demo 라우트로 분리."
|
|
},
|
|
{
|
|
"severity": "recommendation",
|
|
"category": "Performance",
|
|
"location": "backend/src/problem-sets/problem-sets.service.ts:19-28",
|
|
"issue": "getOne() 의 problems include 에 take 없음. 현재 스케일(10문제) 문제 없음. 전과목 번들이 되면 주의.",
|
|
"suggestion": "방어적으로 take: 100 또는 나중에 커서 페이지네이션."
|
|
},
|
|
{
|
|
"severity": "recommendation",
|
|
"category": "Performance",
|
|
"location": "frontend/src/app/stats/page.tsx:9-10",
|
|
"issue": "MasteryPathChart, ForgetCurveChart 둘 다 정적 import. recharts 는 무거운 라이브러리(~140KB gz). 토글당 한쪽만 렌더되므로 dynamic import 가치.",
|
|
"suggestion": "next/dynamic 으로 각 차트 lazy 로드 (ssr:false)."
|
|
},
|
|
{
|
|
"severity": "recommendation",
|
|
"category": "Quality",
|
|
"location": "frontend/src/app/pricing/page.tsx:86-89",
|
|
"issue": "'7일 무료 체험 시작' 버튼이 /register 로만 보내고 실제 체험 로직 없음. 사용자 신뢰 리스크.",
|
|
"suggestion": "Phase 7 전까지 '무료로 시작하기 (Pro 체험 Phase 7 예정)' 로 문구 변경 또는 disabled."
|
|
},
|
|
{
|
|
"severity": "recommendation",
|
|
"category": "Security",
|
|
"location": "frontend/src/app/pricing/page.tsx:108",
|
|
"issue": "<a href='mailto:op@nabomhalang.co.kr'> 평문 노출로 스팸 harvesting 리스크.",
|
|
"suggestion": "문의 폼 또는 JS 난독화."
|
|
},
|
|
{
|
|
"severity": "recommendation",
|
|
"category": "Quality",
|
|
"location": "frontend/src/app/page.tsx:13-23",
|
|
"issue": "Landing 이 useEffect 안에서 hasToken() 체크 후 setReady. SSR 초기는 null 렌더. 초기 flash/CLS 가능.",
|
|
"suggestion": "Next.js middleware 쿠키 기반 redirect 또는 full landing 을 initial state 로."
|
|
}
|
|
],
|
|
"summary": {
|
|
"critical": 0,
|
|
"major": 1,
|
|
"minor": 4,
|
|
"recommendation": 5,
|
|
"verdict_rule_applied": "1 major → REQUEST_CHANGES"
|
|
},
|
|
"next_actions": [
|
|
"Fix P6.major.1 in study-logs.service.ts with NotFoundException guard",
|
|
"Minor fixes 3건 (마스터리 카피, aria-pressed, demo credential 주석) 묶어서 한 commit",
|
|
"Subscription tier enforcement TODO 주석 추가 — Phase 7 연동",
|
|
"Recommendation 5건은 별도 tracking 태스크로 Phase 7 이후 일정"
|
|
]
|
|
}
|