[Refactor] WTH-401: 어드민 출석 캐시 최적화#126
Hidden character warning
Conversation
|
Warning Review limit reached
More reviews will be available in 26 minutes and 34 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough학교 목록 호출을 전용 클라이언트로 통합하고, 공개 동아리 페이지에 Next.js 태그 기반 캐시 재검증을 추가했습니다. admin 쿼리 키 네임스페이스를 ChangesClub 기능 최적화: API 통합, 캐시 전략, 쿼리 키 정리
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-cfste4tp7-weethsite-4975s-projects.vercel.app |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/hooks/mutations/admin/useAdminClubMutations.ts (1)
17-19: ⚡ Quick win
revalidatePublicClub호출 결과를 await 하세요.
revalidatePublicClub는 async 서버 액션인데 await/return 없이 호출되어, 실패 시 처리되지 않은 Promise 거부(unhandled rejection)가 발생할 수 있고 캐시 무효화 완료 시점이 보장되지 않습니다.onSuccess를 async로 만들거나 Promise를 반환하면 React Query가 완료를 기다립니다. 동일한 패턴이useDeleteClubProfileImage(Line 35-37),useDeleteClubBackgroundImage(Line 53-55)에도 적용됩니다.♻️ 제안 수정
- onSuccess: () => { - if (clubId) revalidatePublicClub(clubId); - }, + onSuccess: async () => { + if (clubId) await revalidatePublicClub(clubId); + },🤖 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/hooks/mutations/admin/useAdminClubMutations.ts` around lines 17 - 19, The onSuccess handlers call the async server action revalidatePublicClub without awaiting or returning it, risking unhandled rejections and not guaranteeing cache invalidation completion; update each onSuccess in useAdminClubMutations (the mutation that currently calls revalidatePublicClub(clubId)) and the handlers inside useDeleteClubProfileImage and useDeleteClubBackgroundImage so they either are declared async and await revalidatePublicClub(clubId) or return the Promise (return revalidatePublicClub(clubId)); ensure you reference those exact handlers (onSuccess in the mutation, useDeleteClubProfileImage, useDeleteClubBackgroundImage) and await/return the call to revalidatePublicClub to let React Query wait for completion.
🤖 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 `@src/stores/useCreateClubDraftStore.ts`:
- Around line 19-36: The persist usage around combine(initialState, ...)
(symbols: persist, combine, initialState, setDraft, reset, partialize, name:
'createClubDraft') causes client-side localStorage to hydrate on first render
and can produce SSR hydration mismatches; change the persist options to use
skipHydration: true and add an onRehydrateStorage handler so stored state is
applied only after mount (or ensure components use a mount guard) — i.e., update
the persist options to include skipHydration: true and implement
onRehydrateStorage to rehydrate/update the store after client mount (and keep
the existing partialize/name settings).
---
Nitpick comments:
In `@src/hooks/mutations/admin/useAdminClubMutations.ts`:
- Around line 17-19: The onSuccess handlers call the async server action
revalidatePublicClub without awaiting or returning it, risking unhandled
rejections and not guaranteeing cache invalidation completion; update each
onSuccess in useAdminClubMutations (the mutation that currently calls
revalidatePublicClub(clubId)) and the handlers inside useDeleteClubProfileImage
and useDeleteClubBackgroundImage so they either are declared async and await
revalidatePublicClub(clubId) or return the Promise (return
revalidatePublicClub(clubId)); ensure you reference those exact handlers
(onSuccess in the mutation, useDeleteClubProfileImage,
useDeleteClubBackgroundImage) and await/return the call to revalidatePublicClub
to let React Query wait for completion.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ce9d4b63-eeca-40ff-a1c3-b493fe13a3a7
📒 Files selected for processing (7)
src/app/(private)/(auth)/club/create/page.tsxsrc/app/(private)/[clubId]/admin/club-info/page.tsxsrc/app/(public)/club/[clubId]/page.tsxsrc/hooks/mutations/admin/useAdminClubMutations.tssrc/hooks/queries/admin/useAdminAttendanceQueries.tssrc/lib/actions/club.tssrc/stores/useCreateClubDraftStore.ts
| persist( | ||
| combine(initialState, (set) => ({ | ||
| setDraft: (draft: Partial<CreateClubDraftState>) => set(draft, false, 'setDraft'), | ||
| reset: () => set(initialState, false, 'reset'), | ||
| })), | ||
| { | ||
| name: 'createClubDraft', | ||
| partialize: (state) => ({ | ||
| school: state.school, | ||
| name: state.name, | ||
| description: state.description, | ||
| generation: state.generation, | ||
| phone: state.phone, | ||
| email: state.email, | ||
| contactType: state.contactType, | ||
| }), | ||
| }, | ||
| ), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 스토어 소비처와 'use client' 지시문, RSC 경로 여부 확인
rg -nP "useCreateClubDraft(Values|Actions|Store)" -g '!**/useCreateClubDraftStore.ts'
echo "----- consumer files top lines (check 'use client') -----"
rg -lP "useCreateClubDraft(Values|Actions|Store)" -g '!**/useCreateClubDraftStore.ts' \
| while read -r f; do echo "== $f =="; head -n 3 "$f"; doneRepository: Team-Weeth/weeth-client
Length of output: 1456
Zustand persist 기본 localStorage 사용으로 SSR-하이드레이션 불일치 위험(클라이언트 컴포넌트에서 DOM이 달라질 수 있음)
src/components/auth/hub/CreateClubForm.tsx와 src/components/auth/hub/ClubCreatingPage.tsx는 모두 'use client'라 서버에서 HTML을 만든 뒤 클라이언트에서 하이드레이션합니다. 그런데 src/stores/useCreateClubDraftStore.ts의 persist는 storage/skipHydration 설정이 보이지 않아 기본 localStorage로 동기 복원된 값이 클라이언트 첫 렌더에 반영되며, 서버의 initialState와 DOM이 달라 React 하이드레이션 경고/깜빡임이 날 수 있습니다(특히 CreateClubForm의 RHF defaultValues가 초안 값에 의존하는 경우).
🛠️ 방어 예시: `skipHydration` 사용
{
name: 'createClubDraft',
+ skipHydration: true,
partialize: (state) => ({
school: state.school,
name: state.name,
description: state.description,
generation: state.generation,
phone: state.phone,
email: state.email,
contactType: state.contactType,
}),
},서버/클라이언트 첫 렌더가 동일한 값을 쓰도록 skipHydration + 마운트 후 rehydrate(또는 마운트 가드) 방식으로 타이밍을 제어해 주세요.
🤖 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/stores/useCreateClubDraftStore.ts` around lines 19 - 36, The persist
usage around combine(initialState, ...) (symbols: persist, combine,
initialState, setDraft, reset, partialize, name: 'createClubDraft') causes
client-side localStorage to hydrate on first render and can produce SSR
hydration mismatches; change the persist options to use skipHydration: true and
add an onRehydrateStorage handler so stored state is applied only after mount
(or ensure components use a mount guard) — i.e., update the persist options to
include skipHydration: true and implement onRehydrateStorage to rehydrate/update
the store after client mount (and keep the existing partialize/name settings).
nabbang6
left a comment
There was a problem hiding this comment.
확인이 늦었습니다.........ㅠ.ㅠ 고생하셨어요!!!! 👍
- adminQueryKeys.ts 생성: 어드민 전 도메인(멤버/게시판/일정/출석/클럽) QueryKey를 단일 factory로 통합, 기존 인라인 하드코딩 제거 - 세션 QueryKey 통일: useAdminSessionList의 ['admin', 'sessionList'] → ['admin', 'sessions']로 통일해 출석/일정 간 캐시 공유 및 중복 invalidation 제거 - staleTime/gcTime 명시적 설정: 월간 일정·세션 목록(5분/10분), 출석 상세(0/5분) 추가 - Board mutation 파일 5개를 hooks/queries/admin/ → hooks/mutations/admin/으로 이동해 아키텍처 일관성 확보 - deduplicateSchoolNames 유틸 추출(utils/shared/school.ts): create/page와 club-info/page의 학교명 중복 처리 로직 통합 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 |
|
구현한 기능 Preview: https://weeth-ituoz25mz-weethsite-4975s-projects.vercel.app |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 |
|
구현한 기능 Preview: https://weeth-l7esqfi89-weethsite-4975s-projects.vercel.app |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
|
구현한 기능 Preview: https://weeth-ozbmjc7cy-weethsite-4975s-projects.vercel.app |
✅ PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
1. 어드민 QueryKey Factory 통합 (
adminQueryKeys.ts신규)adminQueryKeys.ts단일 factory로 통합['admin', 'members', clubId]등) 전체 제거, 게시판만 갖던boardQueryKeys.ts흡수·삭제2. 세션 QueryKey 중복 제거
useAdminSessions)과 일정(useAdminSessionList)이 같은 API를 호출하면서 서로 다른 키(['admin', 'sessions']vs['admin', 'sessionList'])를 사용하던 구조 통일3. staleTime / gcTime 명시적 설정
4. Board Mutation 파일 이동
use*BoardMutation.ts5개 파일을hooks/queries/admin/→hooks/mutations/admin/으로 이동해 쿼리/뮤테이션 레이어 경계 정비5. 공개 클럽 페이지 캐시 전략 개선
/club/[clubId])cache: 'no-store'→next: { revalidate: 300, tags: ['public-club-${clubId}'] }(5분 ISR)revalidatePublicClubServer Action으로 즉시 캐시 무효화 연동6. 동아리 생성 폼 상태 유지
useCreateClubDraftStore에 Zustandpersist미들웨어 추가 → 동아리 생성 도중 페이지 이탈 후 복귀 시 입력값 유지7.
universityServerApi타입 안전 API 함수 적용apiServer.get<{ data: School[] }>('/university/schools')직접 호출 →universityServerApi.getSchools()교체 (2곳)utils/shared/school.ts의deduplicateSchoolNames()유틸로 통합📸 스크린샷 or 실행영상
🎸 기타 사항 or 추가 코멘트
pnpm typecheck:@tiptap/extension-*모듈 누락 에러 발생 — 이번 브랜치와 무관한 기존 이슈pnpm build: 동일 원인으로 빌드 실패 — 이번 브랜치 변경 파일에서의 에러 없음 확인pnpm format:check: 이번 브랜치 변경 파일은 모두 통과, 나머지 55개는 기존 미포맷 파일Summary by CodeRabbit
릴리스 노트