Skip to content

feat: 복습 화면 (오답노트/즐겨찾기/최근 푼 문제) - #8

Merged
ginaseo merged 10 commits into
mainfrom
worktree-review-screen
Jul 30, 2026
Merged

feat: 복습 화면 (오답노트/즐겨찾기/최근 푼 문제)#8
ginaseo merged 10 commits into
mainfrom
worktree-review-screen

Conversation

@ginaseo

@ginaseo ginaseo commented Jul 30, 2026

Copy link
Copy Markdown
Owner

요약

  • 오답노트/즐겨찾기/최근 푼 문제 3탭 복습 화면 추가, 전체 다시 풀기(학습모드 재사용)
  • ProgressRepositorygetWrongNotes/getFavorites/removeWrongNote/removeFavorite 추가(기존 시그니처는 무변경, 추가만)
  • QuestionRepository.getTheoryMap() 추가 — practice/page.tsx가 theory_map.json 직접 fetch하던 것도 같이 해소(이전 최종리뷰 이월 항목)
  • 학습모드 화면에 즐겨찾기 버튼 추가(이게 없으면 즐겨찾기 탭이 영원히 비어있었음)
  • superpowers:subagent-driven-development로 5개 태스크 + 최종 브랜치 리뷰(opus) 진행

최종 리뷰에서 발견·수정한 것

  • 탭 전환 race condition: 늦게 도착한 응답이 이미 전환된 다른 탭 화면을 덮어쓸 수 있던 문제 — 요청 토큰 가드로 수정
  • npm run lint가 이 브랜치에서 처음으로 실패(react-hooks/set-state-in-effect) — 계획의 검증 스텝에 lint가 빠져있던 게 원인. loading을 파생값으로 바꾸는 리팩터로 해소
  • 목록 로드 실패가 "데이터 없음"과 구분 안 되던 것 — 최소 console.error 추가

이월(비차단, 기술부채로 기록)

  • WrongNote/Favorite 타입이 db.ts에 익명 인라인으로 중복정의 — 다음에 Repository 건드릴 때 정리
  • 오답노트/즐겨찾기 목록이 questionId 사전순(최근 추가순 아님)
  • 즐겨찾기 탭에서 전체 다시 풀기 시 기존 즐겨찾기 상태가 시드 안 됨 — 언페이버릿 토글 계획에 묶어서 처리 예정
  • "복습 목록으로" 버튼 클릭 시 로딩 스피너 미표시(순수 시각적, 데이터 정합성 문제 없음)

테스트

npx vitest run 51/51 통과, npm run lint 0 에러, npx tsc --noEmit 클린, npm run build 클린 — /, /practice, /review 라우트 정상

참고문서

  • 계획: docs/superpowers/plans/2026-07-31-review-screen.md
  • 설계문서 2절(복습 메뉴 Phase 1 범위)

Summary by CodeRabbit

  • 새로운 기능

    • 오답노트, 즐겨찾기, 최근 풀이를 확인할 수 있는 복습 화면을 추가했습니다.
    • 문제 풀이 중 문제를 즐겨찾기에 추가할 수 있습니다.
    • 복습 목록에서 문제를 제거하거나 전체 문제를 다시 풀 수 있습니다.
    • 홈 화면에서 복습 화면으로 바로 이동할 수 있습니다.
  • 개선 사항

    • 최근 풀이 문제가 최신순으로 중복 없이 표시됩니다.
    • 이론 데이터 로딩 안정성과 재시도 동작을 개선했습니다.
    • 복습 및 즐겨찾기 관련 기능의 테스트를 보강했습니다.

ginaseo and others added 8 commits July 31, 2026 00:07
…failures

The prescribed fix (moving setLoading(true) into the effect/call sites)
still tripped react-hooks/set-state-in-effect, since the rule flags any
function invoked from useEffect that transitively calls a state setter
anywhere in its body, regardless of await position. Restructured to
derive `loading` from a loadedTab comparison instead of an effect-driven
boolean, and moved the fetch's state application into an inline .then()
callback (the pattern React's own docs use), which the rule doesn't trace.
Also logs the actual error via console.error on tab-load failure instead
of silently showing an empty list.
@github-actions github-actions Bot added the enhancement New feature or request label Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ginaseo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 173375f0-3867-473e-9908-0b226336187e

📥 Commits

Reviewing files that changed from the base of the PR and between 6bb3d06 and 4ace3c5.

📒 Files selected for processing (1)
  • src/app/review/page.tsx
📝 Walkthrough

Walkthrough

오답노트·즐겨찾기·최근 풀이를 제공하는 /review 페이지와 목록 컴포넌트를 추가하고, 관련 저장소 API·최근 풀이 추출 함수·즐겨찾기 학습 UI·이론 맵 캐시 조회를 구현했다. 홈과 학습 페이지도 새 흐름에 연결했다.

Changes

복습 화면 구현

Layer / File(s) Summary
복습 데이터 저장소와 조회 계약
src/types/progress.ts, src/repositories/QuestionRepository.ts, src/repositories/ProgressRepository.ts, src/repositories/*test.ts
WrongNote·Favorite 타입과 오답노트·즐겨찾기 조회/삭제 API를 추가하고, 이론 맵을 Promise 기반으로 캐시하며 실패 시 재시도하도록 구현·검증했다.
최근 풀이 추출과 복습 목록
src/lib/recentlySolved.ts, src/lib/recentlySolved.test.ts, src/features/review/ReviewList.tsx
문제별 최신 풀이 시각을 기준으로 중복 없는 최근 문제 ID를 반환하고, 복습 목록의 표시·제거·전체 재도전 UI를 추가했다.
학습 중 즐겨찾기 처리
src/features/practice/QuestionCard.tsx, src/features/practice/PracticeSession.tsx
문제 카드에 즐겨찾기 버튼을 추가하고, 세션에서 상태를 갱신해 진행 저장소에 즐겨찾기를 기록하도록 연결했다.
복습 라우트와 진입 경로
src/app/review/page.tsx, src/app/page.tsx, src/app/practice/page.tsx, docs/superpowers/plans/*
/review에 오답·즐겨찾기·최근 풀이 탭, 목록·활성·완료·오류 단계, 재도전 및 제거 처리를 구현하고 홈 링크와 학습 중 이론 맵 조회 경로를 갱신했다. 구현 계획 문서도 추가했다.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReviewPage
  participant ProgressRepository
  participant QuestionRepository
  participant ReviewList
  participant PracticeSession

  ReviewPage->>ProgressRepository: 탭 데이터 조회
  ReviewPage->>QuestionRepository: 질문과 이론 맵 조회
  QuestionRepository-->>ReviewPage: 복원된 질문 목록 반환
  ReviewPage->>ReviewList: 목록 및 동작 전달
  ReviewList->>PracticeSession: 전체 다시 풀기 실행
  PracticeSession-->>ReviewPage: 복습 완료 결과 전달
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 복습 화면 추가라는 핵심 변경을 간결하게 잘 요약합니다.
Description check ✅ Passed 요약, 테스트, 참고문서 섹션이 모두 있으며 주요 변경과 검증 결과를 충분히 담고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-review-screen

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/features/practice/PracticeSession.tsx (1)

23-23: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

기존 즐겨찾기 상태를 세션 초기값에 반영하세요.

복습 화면의 즐겨찾기 탭에서 재도전하더라도 favorited는 항상 빈 맵으로 시작하므로, 이미 저장된 문제에 ☆ 즐겨찾기가 표시됩니다. 저장소의 기존 즐겨찾기 ID를 초기 상태로 주입하거나 세션 시작 전에 조회해야 합니다.

Also applies to: 109-111

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/practice/PracticeSession.tsx` at line 23, Initialize the
favorited state in PracticeSession using the existing persisted favorite
question IDs instead of always starting with an empty map. Load or derive those
IDs before the session renders, map each ID to true, and preserve the current
toggle behavior through setFavorited.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-07-31-review-screen.md`:
- Around line 927-933: Update the Task 5 code block in the self-review section
to match the current review page implementation: document derived loading state
and the request-token guard in loadTab, rather than a standalone loading state
and unguarded requests. Ensure the documented implementation preserves
protection against tab response races.

In `@src/app/review/page.tsx`:
- Around line 106-112: Update handleRemove so setQuestions runs only after the
relevant progressRepository removal succeeds. Do not swallow removal failures
and continue to the state update; preserve the existing tab-specific
removeWrongNote and removeFavorite behavior while keeping failed items in the
list.

In `@src/features/practice/PracticeSession.tsx`:
- Around line 62-67: Update toggleFavorite to support both states: when
favorited[current] is true, clear that entry and call
progressRepository.removeFavorite with question.questionId; otherwise preserve
the existing state update and progressRepository.addFavorite flow.
- Around line 62-67: Update toggleFavorite in
src/features/practice/PracticeSession.tsx#L62-L67 to call addFavorite when the
current question is not favorited and removeFavorite when it is, updating state
for both outcomes while preserving error handling. In
src/features/practice/QuestionCard.tsx#L37-L43, disable the button only while
the favorite operation is processing and provide an appropriate
remove/unfavorite label when the question is already favorited.

---

Nitpick comments:
In `@src/features/practice/PracticeSession.tsx`:
- Line 23: Initialize the favorited state in PracticeSession using the existing
persisted favorite question IDs instead of always starting with an empty map.
Load or derive those IDs before the session renders, map each ID to true, and
preserve the current toggle behavior through setFavorited.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a8315429-a937-49ee-8261-1c3d56858120

📥 Commits

Reviewing files that changed from the base of the PR and between 16e7a07 and 6bb3d06.

📒 Files selected for processing (14)
  • docs/superpowers/plans/2026-07-31-review-screen.md
  • src/app/page.tsx
  • src/app/practice/page.tsx
  • src/app/review/page.tsx
  • src/features/practice/PracticeSession.tsx
  • src/features/practice/QuestionCard.tsx
  • src/features/review/ReviewList.tsx
  • src/lib/recentlySolved.test.ts
  • src/lib/recentlySolved.ts
  • src/repositories/ProgressRepository.test.ts
  • src/repositories/ProgressRepository.ts
  • src/repositories/QuestionRepository.test.ts
  • src/repositories/QuestionRepository.ts
  • src/types/progress.ts

Comment thread docs/superpowers/plans/2026-07-31-review-screen.md
Comment thread src/app/review/page.tsx
Comment on lines +62 to +67
function toggleFavorite() {
if (favorited[current]) return;
setFavorited((prev) => ({ ...prev, [current]: true }));
progressRepository
.addFavorite(question.questionId)
.catch((err) => console.error("addFavorite failed:", err));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

toggleFavorite에 해제 경로가 없습니다.

이미 즐겨찾기된 질문은 즉시 반환하고 addFavorite만 호출합니다. ProgressRepositoryremoveFavorite가 있으므로 현재 상태가 true이면 삭제하도록 연결해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/practice/PracticeSession.tsx` around lines 62 - 67, Update
toggleFavorite to support both states: when favorited[current] is true, clear
that entry and call progressRepository.removeFavorite with question.questionId;
otherwise preserve the existing state update and progressRepository.addFavorite
flow.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

즐겨찾기 토글을 추가/삭제 모두 지원하도록 맞춰야 합니다.

현재 상태가 true이면 아무 동작도 하지 않고, UI도 버튼을 비활성화합니다. ProgressRepository.removeFavorite를 사용해 true 상태에서는 삭제하고, 버튼은 처리 중일 때만 비활성화하도록 변경하세요.

  • src/features/practice/PracticeSession.tsx#L62-L67: favorited 상태에 따라 addFavorite 또는 removeFavorite를 호출합니다.
  • src/features/practice/QuestionCard.tsx#L37-L43: disabled 조건을 저장 중 상태로 변경하고 해제 라벨을 제공합니다.
📍 Affects 2 files
  • src/features/practice/PracticeSession.tsx#L62-L67 (this comment)
  • src/features/practice/QuestionCard.tsx#L37-L43
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/practice/PracticeSession.tsx` around lines 62 - 67, Update
toggleFavorite in src/features/practice/PracticeSession.tsx#L62-L67 to call
addFavorite when the current question is not favorited and removeFavorite when
it is, updating state for both outcomes while preserving error handling. In
src/features/practice/QuestionCard.tsx#L37-L43, disable the button only while
the favorite operation is processing and provide an appropriate
remove/unfavorite label when the question is already favorited.

ginaseo added 2 commits July 31, 2026 01:14
Codex 리뷰 지적(SDD 최종리뷰 minor7과 동일 지점, 두 리뷰어 독립 확인) — handleRemove가 기존 loadTab/effect의 latestRequestId 가드에 참여 안 해서, 삭제 완료 전 탭 전환하면 새 탭의 목록에 필터가 잘못 적용될 수 있었음
CodeRabbit 리뷰 지적(SDD 최종리뷰 ledger2와 동일 지점, 두 리뷰어 독립 확인) — removeWrongNote/removeFavorite 실패를 .catch로 삼키고도 무조건 목록에서 필터링하던 것을 실패 시 조기 반환하도록 수정
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant