Skip to content

[REFACTOR] 퀴즈 등록 모달 메타 로드를 useEffect 페칭→useTransition으로 개선 #872#925

Merged
GwakSiyun merged 3 commits into
developfrom
refactor/quiz-formMetaFetch#872
Jul 18, 2026
Merged

[REFACTOR] 퀴즈 등록 모달 메타 로드를 useEffect 페칭→useTransition으로 개선 #872#925
GwakSiyun merged 3 commits into
developfrom
refactor/quiz-formMetaFetch#872

Conversation

@hyun030514

@hyun030514 hyun030514 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

📋 PR 타입

  • feature
  • fix
  • style
  • refactor
  • docs
  • test
  • design
  • chore
  • merge

🙋 관련 역할

공통 (강사·관리자 퀴즈 등록 모달)

📝 작업 내용

  • 강의 선택 시 메타(섹션 주차·takenWeeks) 조회를 반응형 useEffect([courseId])'연결 강의' onChange 핸들러의 useTransition 호출로 이동 (렌더링 워터폴 제거, §4/CodeRabbit path instruction)
  • 취소 안전성: transition엔 cleanup 취소가 없어 metaReqRef(요청 id)로 stale 응답 무시로 대체 — 빠른 강의 전환 시 옛 응답이 새 선택을 덮지 않게
  • presetCourseId(개별 강의 페이지·강의 고정)만 마운트 1회 로드로 축소 (사용자 강의 선택은 onChange가 처리)
  • metaLoadingisPending 통합, 에러 토스트(§0.1)·1주1퀴즈 중복차단 동작 보존

✅ 작업 체크리스트

  • 브랜치 명명 규칙 준수
  • 커밋 컨벤션 준수
  • 불필요한 console 제거
  • 테스트 완료
  • 민감 정보 포함 여부 확인

🔗 연관 이슈

Closes #872

📸 스크린샷 (선택)

UI 변경 없음 (데이터 페칭 구조만 변경, 동작 보존).

💬 리뷰 포인트

  • presetCourseId 트리거는 이슈의 두 안(서버 props 선주입 vs 최소 트리거) 중 최소 마운트 트리거 채택 — 반응형 워터폴 제거가 목적이고 서버 props안은 동적 강의 선택 UX와 부딪혀서(이슈 참고).
  • metaReqRef stale-guard가 기존 cancelled 플래그의 취소 안전성을 대체하는지.

📝 추가 메모

  • 회귀 테스트 18/18 통과 (presetCourseId·adminMeta 라우팅·에러토스트·주차소진 noWeeksAvailable 포함)
  • next build 성공 · tsc 0 · lint 0

Summary by CodeRabbit

  • 버그 수정
    • 연결 강의를 선택하면 주차 정보가 즉시 로드되도록 개선했습니다.
    • 강의를 빠르게 바꿀 때 이전 요청의 응답이 최신 선택을 덮어쓰지 않도록 수정했습니다.
    • 메타데이터 로딩 중 상태를 더 정확히 반영하도록 개선했습니다.
    • 강의 정보 로드 실패 시 오류 안내가 표시됩니다.
    • 강사 변경 시 이전 강의 관련 로딩이 올바르게 초기화됩니다.

- 강의 선택 시 메타(섹션 주차·takenWeeks) 조회를 반응형 useEffect([courseId])에서
  '연결 강의' onChange 핸들러의 useTransition 호출로 이동 (렌더 워터폴 제거, §4)
- 취소 안전성: transition엔 cleanup 취소가 없어 metaReqRef로 stale 응답 무시로 대체
  (빠른 강의 전환 시 옛 응답이 새 선택을 덮지 않게)
- presetCourseId(개별 강의 페이지·강의 고정)만 마운트 1회 로드로 축소
- metaLoading에 isPending 통합, 에러 토스트(§0.1)·1주1퀴즈 중복차단 동작 보존
- 회귀 테스트 18/18 통과 (presetCourseId·adminMeta 라우팅·에러토스트·주차소진)
@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hard-click-front-end Ready Ready Preview, Comment Jul 18, 2026 11:23am

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 56 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: ASSERTIVE

Plan: Pro Plus

Run ID: 8638f2ea-5353-44aa-9473-e9c52967ee27

📥 Commits

Reviewing files that changed from the base of the PR and between 5d90b8a and 06aaf77.

📒 Files selected for processing (1)
  • hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx
📝 Walkthrough

Walkthrough

QuizFormModal의 강의 메타 조회를 렌더 의존 useEffect에서 명시적 loadMeta 호출로 전환했다. 요청 순번으로 stale 응답을 무시하고, 강의 선택 시 메타 조회를 즉시 시작한다.

Changes

퀴즈 메타 로딩 흐름

Layer / File(s) Summary
요청 안전 메타 로딩
hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx
loadMetauseTransition으로 메타 조회를 수행하고, 요청 순번이 다른 응답의 상태·토스트 반영을 건너뛴다. presetCourseId는 마운트 시 1회 조회한다.
강의 선택 트리거 연결
hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx
강사 변경 시 진행 중인 요청을 무효화하고, 연결 강의 선택 시 courseIdweek를 갱신한 뒤 메타 조회를 즉시 호출한다.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant QuizFormModal
  participant QuizFormMetaAction
  participant Toast
  User->>QuizFormModal: 연결 강의 선택
  QuizFormModal->>QuizFormMetaAction: loadMeta(cid)
  QuizFormMetaAction-->>QuizFormModal: 주차 메타 응답
  QuizFormModal-->>User: 메타 및 로딩 상태 갱신
  QuizFormMetaAction-->>QuizFormModal: 오류 응답
  QuizFormModal->>Toast: 오류 토스트 표시
Loading

Possibly related PRs

Suggested reviewers: gwaksiyun

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 퀴즈 등록 모달의 메타 로딩을 useEffect에서 useTransition 기반으로 바꾼 핵심 변경을 정확히 요약합니다.
Linked Issues check ✅ Passed 강의 선택 onChange로 메타 조회를 옮기고, presetCourseId 초기 로드와 stale 응답 무시·토스트 보존까지 반영했습니다.
Out of Scope Changes check ✅ Passed 변경은 퀴즈 등록 모달 메타 로딩 패턴 개선과 관련된 범위에만 머물러 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/quiz-formMetaFetch#872

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: 2

🤖 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 `@hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx`:
- Around line 164-167: QuizFormModal의 presetCourseId 메타 조회 effect에서 언마운트 시 진행 중인
요청이 결과를 반영하지 않도록 정리하세요. 가능하면 고정 강의 메타를 서버에서 조회해 props로 전달하고, effect를 유지한다면
cleanup에서 요청 번호를 증가시켜 loadMeta의 성공·실패 처리와 토스트가 무효화된 요청에 대해 실행되지 않도록 하세요.
- Around line 138-160: Update loadMeta to track pending state for the current
reqId rather than relying solely on the shared isMetaPending transition state.
Mark the active request as pending when it starts, and clear loading only when
that same request resolves or rejects; stale responses must not change the
current request’s loading state. Use the existing metaReqRef and metadata
loading state so an earlier course request cannot keep the current course’s week
selector disabled.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: d337c750-e1df-40f3-9b7c-642766cdbbf8

📥 Commits

Reviewing files that changed from the base of the PR and between 748ed5e and b721151.

📒 Files selected for processing (1)
  • hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx

Comment on lines +138 to +160
const [isMetaPending, startMetaTransition] = useTransition();
// 강의 선택 시 실제 섹션(주차)·이미 쓴 주차를 서버에서 로드. useEffect 반응형 페칭(렌더 워터폴) 대신
// 이벤트 핸들러('연결 강의' onChange)에서 useTransition으로 호출(§4). transition엔 cleanup 취소가
// 없어, 취소 안전성은 metaReqRef로 stale 응답 무시로 대체(빠른 강의 전환 시 옛 응답이 새 선택을 덮지 않게).
const loadMeta = (cid: number) => {
if (mode !== 'create' || cid <= 0) return;
const reqId = ++metaReqRef.current;
// 관리자(adminMeta)는 소유자무관 관리자 목록으로 takenWeeks 집계 —
// 강사 엔드포인트(/api/instructor/quizzes)는 로그인 관리자 소유 퀴즈만 반환(0개)이라 1주1퀴즈 중복차단이 안 됨.
const metaAction = adminMeta
? getAdminQuizFormMetaAction
: getQuizFormMetaAction;
metaAction(courseId)
.then((m) => {
if (!cancelled) setMeta({ courseId, ...m });
})
.catch(() => {
startMetaTransition(async () => {
try {
const m = await metaAction(cid);
if (metaReqRef.current === reqId) setMeta({ courseId: cid, ...m });
} catch {
// 실패를 빈 주차로 폴백하면 '등록 가능한 주차가 없습니다'로 오표시돼(§0.1),
// 조용히 삼키지 않고 사용자에게 알린다. 주차 드롭다운은 로딩 상태로 남아 오등록을 막는다.
if (!cancelled)
if (metaReqRef.current === reqId)
toast.error('주차 정보를 불러오지 못했습니다. 잠시 후 다시 시도해주세요.');
});
return () => {
cancelled = true;
};
}, [courseId, mode, adminMeta]);
// 로딩 = 선택 강의 기준 meta가 아직 안 옴 (파생, 동기 setState 없음).
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx"

echo "== outline =="
ast-grep outline "$file" --view expanded || true

echo
echo "== relevant lines =="
nl -ba "$file" | sed -n '110,210p'

Repository: Hard-Click/Hard-Click-FrontEnd

Length of output: 528


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx"

echo "== lines 130-185 =="
sed -n '130,185p' "$file"

echo
echo "== search for metaLoading / metaReqRef / useTransition =="
grep -nE 'useTransition|metaReqRef|metaLoading|loadMeta|startMetaTransition|isMetaPending' "$file" || true

Repository: Hard-Click/Hard-Click-FrontEnd

Length of output: 3398


🌐 Web query:

React useTransition async callback pending promise ignored startTransition docs

💡 Result:

In React 19 and later, the useTransition hook has been improved to automatically handle state updates that occur after an await within an async transition [1]. If you are using the startTransition function returned by useTransition, you no longer need to manually wrap state updates that follow an asynchronous request in an additional startTransition call [1]. These updates are automatically included in the transition [1]. However, this improvement applies specifically to the startTransition provided by the useTransition hook [1]. If you are using the standalone React.startTransition function (often used outside of components or where useTransition is unavailable), you must still manually wrap any state updates that occur after an await in a new startTransition call [2][1]. The requirement to wrap updates in earlier versions of React or when using standalone startTransition arises because React loses the asynchronous context when an await expression is encountered [3]. Without this context, React cannot automatically associate subsequent state updates with the original transition, causing them to be treated as standard, urgent updates instead of non-blocking transitions [4][3]. Summary of Behavior: 1. useTransition.startTransition: Supports async functions natively. State updates after await are automatically part of the transition [1]. 2. React.startTransition (standalone): Does not automatically track context after await. State updates after await must be wrapped in another startTransition [2][1]. If you find that your state updates after an await are not being treated as transitions, ensure you are utilizing the startTransition returned by useTransition and check that you are not accidentally relying on the standalone version [2][1].

Citations:


로딩 상태를 현재 요청 기준으로 분리하세요. isMetaPending만 쓰면 이전 강의의 느린 요청이 끝날 때까지 주차 선택이 계속 비활성화됩니다. reqId로 현재 요청의 pending을 따로 추적하고, 해당 요청이 끝날 때만 로딩을 해제하세요.

🤖 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 `@hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx` around
lines 138 - 160, Update loadMeta to track pending state for the current reqId
rather than relying solely on the shared isMetaPending transition state. Mark
the active request as pending when it starts, and clear loading only when that
same request resolves or rejects; stale responses must not change the current
request’s loading state. Use the existing metaReqRef and metadata loading state
so an earlier course request cannot keep the current course’s week selector
disabled.

@hyun030514
hyun030514 requested a review from GwakSiyun July 18, 2026 10:15
@hyun030514 hyun030514 self-assigned this Jul 18, 2026
@hyun030514 hyun030514 added this to the M1 기능 수정 milestone Jul 18, 2026
- metaLoading이 isMetaPending을 OR로 참조해, 빠른 강의 전환으로 무효화된 stale transition이
  아직 pending이면 현재 선택 로드 후에도 주차 드롭다운이 계속 잠기던 회귀 수정
- 파생 조건(meta.courseId !== courseId)만으로 로딩 판별 — 더 정확하고 stale 커플링 없음
- 코드리뷰(적대적 검증) 반영, 회귀 테스트 18/18 유지

@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.

♻️ Duplicate comments (1)
hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx (1)

164-167: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

클라이언트 렌더링 시점의 데이터 페칭 패턴 개선 및 언마운트 처리 추가

useEffect를 통한 클라이언트 데이터 페칭은 워터폴을 유발하므로, 서버 컴포넌트에서 presetCourseId에 해당하는 메타데이터를 사전 조회하여 props로 주입하는 패턴을 권장합니다.
부득이하게 현행 로딩 방식을 유지해야 한다면, 컴포넌트 언마운트 시 진행 중인 비동기 요청이 백그라운드에서 완료되어 불필요한 에러 토스트가 노출되거나 상태가 업데이트되는 것을 막기 위해 반드시 cleanup 함수를 추가해야 합니다.

As per path instructions, useEffect + useState 데이터 페칭을 지적한다(서버 조회 권장).

🛠 제안하는 수정안
   useEffect(() => {
     if (presetCourseId) loadMeta(presetCourseId);
+    return () => {
+      metaReqRef.current++;
+    };
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, []);
🤖 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 `@hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx` around
lines 164 - 167, Update the QuizFormModal data-loading flow around the useEffect
that calls loadMeta so presetCourseId metadata is fetched in the server
component and passed into the modal as props, removing the client-side
fetch/state initialization where possible. If client fetching must remain, add
effect cleanup and guard the loadMeta completion/error handling so unmounted
components cannot update state or show error toasts.

Source: Path instructions

🤖 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.

Duplicate comments:
In `@hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx`:
- Around line 164-167: Update the QuizFormModal data-loading flow around the
useEffect that calls loadMeta so presetCourseId metadata is fetched in the
server component and passed into the modal as props, removing the client-side
fetch/state initialization where possible. If client fetching must remain, add
effect cleanup and guard the loadMeta completion/error handling so unmounted
components cannot update state or show error toasts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 51e809cc-2f5c-4f15-a8f0-962d7d47212d

📥 Commits

Reviewing files that changed from the base of the PR and between b721151 and 5d90b8a.

📒 Files selected for processing (1)
  • hard-click-frontend/src/features/quizzes/components/QuizFormModal.tsx

- 모달이 닫힌 뒤 진행 중이던 preset 메타 로드의 stale 응답이 에러 토스트를 띄우거나
  setMeta를 호출하는 것 방지 — cleanup에서 metaReqRef 증가로 무효화
  (기존 useEffect cleanup의 cancelled 가드를 언마운트 경로에서 복원)
- CodeRabbit 리뷰 반영, 회귀 테스트 18/18 유지

@GwakSiyun GwakSiyun left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

확인했습니다.

@GwakSiyun
GwakSiyun merged commit 9dc509b into develop Jul 18, 2026
3 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in FLOWN Project Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[REFACTOR] 퀴즈 등록 모달 메타 로드를 useEffect 페칭에서 useTransition/서버조회로 개선

2 participants