refactor: 흩어진 서버 prefetch 정리로 중복 API 호출 제거 - #511
Conversation
…-query-call # Conflicts: # apps/web/src/app/home/page.tsx # apps/web/src/hooks/useNotificationSSE.ts
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 59 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough사용자 조회를 RootLayout의 React Query hydration으로 통합했습니다. 라우트별 사용자 prefetch를 제거했습니다. 보호된 레이아웃은 JWT 역할을 검사합니다. 사용자 및 알림 쿼리 키를 중앙 상수로 통일했습니다. Changes인증 및 쿼리 흐름 정리
Estimated code review effort: 3 (Moderate) | ~25 minutes Mergeability Score: 🟡 Moderate · up to The member-only route checks now rely on token claims instead of confirming the account with the server, so server-invalidated sessions or deleted accounts may reach these pages and fail later without the previous session-expiry behavior. Merge should wait until that handling is restored or the changed behavior is explicitly accepted. Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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 |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
apps/web/src/app/layout.tsx (1)
6-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
React를 타입 전용으로 가져오세요.Line 6의
React는React.ReactNode타입에만 사용됩니다.import type를 사용하세요.수정 예시
-import React from 'react'; +import type React from 'react';As per coding guidelines:
use import type for type-only imports.🤖 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 `@apps/web/src/app/layout.tsx` at line 6, Update the React import in the layout module to use a type-only import, since it is referenced only for the React.ReactNode type; leave the type usage unchanged.Source: Coding guidelines
apps/web/src/app/mypage/withdraw/layout.tsx (1)
9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value컴포넌트 Props 타입을 분리하세요.
MyPageMemberOnlyLayout의 Props를MyPageMemberOnlyLayoutProps타입으로 선언하세요. 인라인 객체 타입은 컴포넌트 Props 명명 규칙과 다릅니다.수정 예시
-async function MyPageMemberOnlyLayout({ children }: { children: React.ReactNode }) { +type MyPageMemberOnlyLayoutProps = { + children: React.ReactNode; +}; + +async function MyPageMemberOnlyLayout({ children }: MyPageMemberOnlyLayoutProps) {As per coding guidelines:
use {ComponentName}Props for component props.🤖 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 `@apps/web/src/app/mypage/withdraw/layout.tsx` at line 9, Declare a named MyPageMemberOnlyLayoutProps type for the children prop and update MyPageMemberOnlyLayout to use that type instead of an inline object type, following the component props naming convention.Source: Coding guidelines
🤖 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 `@apps/web/src/app/archive/wish/layout.tsx`:
- Around line 18-25: Extend the server-side authentication flow in
apps/web/src/app/archive/wish/layout.tsx lines 18-25 so server rejection of
authentication or account status redirects through the existing
getLoginPath(..., QUERY_ACTION.VALUE.SESSION_EXPIRED) flow instead of allowing
MEMBER access; preserve the existing WishLoginRequired behavior for non-member
roles. Apply the same server account-state validation and session-expired
redirect in apps/web/src/app/mypage/withdraw/layout.tsx lines 13-23, reusing the
existing getMe-based or equivalent rejection handling in each layout.
In `@apps/web/src/app/notification/page.tsx`:
- Around line 7-17: 알림 페이지의 주 콘텐츠를 감싸는 루트 `<div>`를 `<main>` 요소로 변경하세요. `Header`와
`NotificationContent`를 포함한 기존 구조와 클래스명은 그대로 유지하세요.
- Line 3: Update the NotificationContent import in the notification page to use
the project-standard absolute alias path
"`@/app/notification/_components/NotificationContent`" instead of a relative path.
---
Nitpick comments:
In `@apps/web/src/app/layout.tsx`:
- Line 6: Update the React import in the layout module to use a type-only
import, since it is referenced only for the React.ReactNode type; leave the type
usage unchanged.
In `@apps/web/src/app/mypage/withdraw/layout.tsx`:
- Line 9: Declare a named MyPageMemberOnlyLayoutProps type for the children prop
and update MyPageMemberOnlyLayout to use that type instead of an inline object
type, following the component props naming convention.
🪄 Autofix
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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0dd4cd5d-7c3e-4e1a-8019-2e871c2b004b
📒 Files selected for processing (21)
apps/web/e2e/helpers/fakeJwt.tsapps/web/src/apis/getMe.tsapps/web/src/app/archive/wish/layout.tsxapps/web/src/app/auth/callback/[provider]/_hooks/usePostSocialLogin.tsapps/web/src/app/home/page.tsxapps/web/src/app/layout.tsxapps/web/src/app/login/_hooks/usePostGuestLogin.tsapps/web/src/app/mypage/edit/_hooks/usePatchMe.tsapps/web/src/app/mypage/edit/page.tsxapps/web/src/app/mypage/page.tsxapps/web/src/app/mypage/withdraw/layout.tsxapps/web/src/app/notification/_components/NotificationContent.tsxapps/web/src/app/notification/_hooks/useGetNotifications.tsapps/web/src/app/notification/_hooks/usePostNotificationsRead.tsapps/web/src/app/notification/page.tsxapps/web/src/app/tournament/[id]/create/page.tsxapps/web/src/app/tournament/join/[id]/page.tsxapps/web/src/components/notification-sse-provider/index.tsxapps/web/src/consts/queryKeys.tsapps/web/src/hooks/useGetMe.tsapps/web/src/hooks/useNotificationSSE.ts
작업 요약
/users/me조회를 1번으로 줄입니다 (root layout prefetch 통합)작업 세부 내용
1.
['me']서버 prefetch를 root layout 한 곳으로['me']를 서버에서 prefetch하는 곳이 6개 라우트에 흩어져 있었고, 바텀탭이 나머지 탭 라우트를 전부 프리페치합니다. 라우트 프리페치는 각각 독립된 RSC 요청이라 프리페치되는 라우트 수만큼getMe가 다시 나갔습니다 (이슈의 로그인 직후 4회).클라 내비게이션·라우트 프리페치는 변경된 세그먼트만 렌더하고 상위 layout은 재실행하지 않으므로, prefetch를 root layout 한 곳으로 올리고 하위 라우트의 hydration용 prefetch 5곳(home, mypage, mypage/edit, tournament create/join)을 제거했습니다.
await없이 pending promise를 dehydrate해 스트리밍하므로 렌더를 블로킹하지 않습니다 (홈은 기존의await prefetchQuery블로킹도 함께 사라졌습니다).#470(serverApi 401 → 로그인 redirect)과의 관계 — dev 머지 과정에서 홈의 me 조회가
serverPrefetch(redirect 통과) 패턴으로 바뀌어 있었는데, root layout에서는 의도적으로 redirect를 삼키는prefetchQuery를 유지했습니다. root layout은 로그인 페이지까지 감싸기 때문에, "만료 전이지만 서버가 무효화한 토큰"으로 로그인 페이지에 진입하면getMe401 → 로그인 redirect → 다시 root layout → 다시 401… 의 서버사이드 무한 리다이렉트 루프가 생길 수 있어서입니다. 이 케이스의 세션 만료 탈출은 클라이언트 인터셉터(refresh 실패 → 세션 만료 처리) 경로가 담당합니다.실측 — 로그인 직후 서버 로그 기준
/users/me4회 → 1회:2. 쿼리 정의 단일화 —
getMeQueryOptions+QUERY_KEYSapis/getMe.ts에queryOptions()로 키·fetcher·staleTime(5분)을 한 덩어리로 정의하고,useGetMe·SSE provider·root layout이 공유합니다. SSE provider가 기본 staleTime(60초)으로 조기 refetch하던 어긋남도 함께 해소됩니다.['me'],['notifications']리터럴을QUERY_KEYS.USER.ME,QUERY_KEYS.NOTIFICATION.LIST로 등록해 invalidate/refetch 지점까지 전부 교체했습니다.3. 멤버 gating을 토큰 role 클레임 판별로 전환
archive/wish·mypage/withdrawlayout의 멤버 확인은getMe응답의identityType하나를 보는 용도였는데, 이 정보는 access token의role클레임에 이미 있습니다.getRoleFromToken(기존getIsGuest와 같은 방식)으로 바꿔 위시 탭 프리페치에 남아 있던getMe1회도 제거했습니다.SESSION_EXPIRED로그인 이동. 프록시가 같은isTokenValid로 먼저 걸러주므로 인증 라우트에서는 도달하지 않는 경로입니다WishLoginRequired, 탈퇴 페이지는 로그인 대신 마이페이지로 복귀 (링크 자체가 멤버에게만 노출되는 페이지라 이상 진입 방어 성격이고, "로그인하고 탈퇴하세요" 흐름이 어색해 변경)role: 'MEMBER'클레임을 추가해 SSR 목(회원 고정)과 정합을 맞췄습니다4. 알림·홈의 중복 조회 정리
AlarmHeaderIcon)가 같은 키로 이미 목록을 받아두고 SSE가 신선도를 유지하는데, 페이지 진입마다 RSC prefetch가 같은 데이터를 또 받아왔습니다. 클라 캐시 재사용으로 전환하고, 정적 골격(배경·헤더·스크롤 컨테이너)은 RSC인page.tsx로 올려 콘텐츠만 클라 컴포넌트로 남겼습니다.home/page.tsx와 자식TournamentList가 같은{limit: 3}토너먼트 prefetch를 각각 하고 있어(요청은 dedupe되지만 코드 중복) 소비처인TournamentList쪽만 남겼습니다.연관 이슈
closes #491
Summary by CodeRabbit