feat: 라우트별 lazy import 도입 및 splitting 적용#196
Conversation
Walkthrough라우팅 트리를 React.lazy와 Suspense로 감싸고 라우트 수준 코드 분할을 적용했습니다. 다수의 정적 페이지 임포트를 동적 import로 교체했고, 비동기 로드 중 표시되는 RouteLoadingFallback 컴포넌트를 추가했습니다. Layout의 Suspense fallback 표기와 일부 CSS 변수 참조 표기가 조정되었고, auth 초기화/수화(hydration) 로직의 중복 호출 방지 및 토큰 검사 흐름이 개선되었습니다. 그 외 여러 UI 컴포넌트에서 이미지 로딩/우선순위, 알림/카운트 훅, 홈용 공지 훅 등 기능 파일이 추가·변경되었습니다. Possibly related PRs
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/layout/index.tsx (1)
31-42:⚠️ Potential issue | 🟡 Minor
Suspense를main안으로 옮겨서 로딩 중 레이아웃 일관성 유지하기현재
Outletsuspend 시Suspensefallback이 전체main을 대체하면서contentClassName, 헤더 padding,pb-19여백이 모두 사라집니다.src/App.tsx에서contentClassName="bg-indigo-0"를 넘기는 라우트에서 로딩 중 배경색이 갑자기 바뀌는 문제가 발생합니다.
Suspense를main안쪽으로 이동하면 레이아웃 클래스가 유지되어 안정적입니다.제안 수정
- <Suspense fallback={<RouteLoadingFallback />}> - <main - className={cn( - 'bg-background box-border flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden', - hasHeader && (isInfoHeader ? 'pt-15' : isManagerHeader ? 'pt-(--manager-header-height)' : 'pt-11'), - showBottomNav && 'pb-19', - contentClassName - )} - > - <Outlet /> - </main> - </Suspense> + <main + className={cn( + 'bg-background box-border flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden', + hasHeader && (isInfoHeader ? 'pt-15' : isManagerHeader ? 'pt-(--manager-header-height)' : 'pt-11'), + showBottomNav && 'pb-19', + contentClassName + )} + > + <Suspense fallback={<RouteLoadingFallback />}> + <Outlet /> + </Suspense> + </main>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/layout/index.tsx` around lines 31 - 42, The Suspense fallback currently wraps the entire main element causing layout classes (contentClassName, header paddings computed from hasHeader/isInfoHeader/isManagerHeader, and showBottomNav pb-19) to be replaced during loading; move the Suspense so main always renders and wrap only the Outlet with Suspense (using RouteLoadingFallback) so the main DOM and its className computation remain intact while Outlet suspends.
🧹 Nitpick comments (1)
src/App.tsx (1)
14-49: lazy import도@/별칭으로 통일해 주세요.이 블록만 상대 경로를 쓰고 있어서 파일 이동이나 폴더 정리 때 깨지기 쉽습니다. 정적 import와 동일하게
@/pages/...로 맞추는 편이 유지보수에 유리합니다.📦 예시
-const ConfirmStep = lazy(() => import('./pages/Auth/SignUp/ConfirmStep')); +const ConfirmStep = lazy(() => import('@/pages/Auth/SignUp/ConfirmStep'));As per coding guidelines,
@/*alias를 상대 경로 대신 사용해야 합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/App.tsx` around lines 14 - 49, The lazy imports for components like ConfirmStep, FinishStep, NameStep, StudentIdStep, TermStep, UniversityStep, ChatListPage, ChatRoom, ApplicationPage, ApplyCompletePage, ClubFeePage, ClubDetail, ClubList, ClubSearch, CouncilDetail, CouncilNotice, GuidePage, LicensePage, MarketingPolicyPage, PrivacyPolicyPage, TermsPage, ManagedAccount, ManagedApplicationDetail, ManagedApplicationList, ManagedClubDetail, ManagedClubInfo, ManagedClubList, ManagedMemberApplicationDetail, ManagedMemberList, ManagedRecruitment, ManagedRecruitmentForm, ManagedRecruitmentWrite, Schedule, Timer, MyPage, and Profile use relative paths and should be switched to the project alias; update each lazy(() => import('./pages/...')) to use the alias form lazy(() => import('@/pages/...')) so all imports match the alias-based convention and avoid breakage during refactors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/common/RouteLoadingFallback.tsx`:
- Around line 9-18: The loading markup in RouteLoadingFallback lacks readable
text for screen readers; update the component (RouteLoadingFallback) so the
visible spinner div has aria-hidden="true" (e.g., the element with className
"h-8 w-8 animate-spin ...") and add a visually-hidden / sr-only text node inside
the role="status" container (e.g., "Loading…" or "로딩 중…") so screen readers
announce the status while the spinner is hidden from assistive tech.
---
Outside diff comments:
In `@src/components/layout/index.tsx`:
- Around line 31-42: The Suspense fallback currently wraps the entire main
element causing layout classes (contentClassName, header paddings computed from
hasHeader/isInfoHeader/isManagerHeader, and showBottomNav pb-19) to be replaced
during loading; move the Suspense so main always renders and wrap only the
Outlet with Suspense (using RouteLoadingFallback) so the main DOM and its
className computation remain intact while Outlet suspends.
---
Nitpick comments:
In `@src/App.tsx`:
- Around line 14-49: The lazy imports for components like ConfirmStep,
FinishStep, NameStep, StudentIdStep, TermStep, UniversityStep, ChatListPage,
ChatRoom, ApplicationPage, ApplyCompletePage, ClubFeePage, ClubDetail, ClubList,
ClubSearch, CouncilDetail, CouncilNotice, GuidePage, LicensePage,
MarketingPolicyPage, PrivacyPolicyPage, TermsPage, ManagedAccount,
ManagedApplicationDetail, ManagedApplicationList, ManagedClubDetail,
ManagedClubInfo, ManagedClubList, ManagedMemberApplicationDetail,
ManagedMemberList, ManagedRecruitment, ManagedRecruitmentForm,
ManagedRecruitmentWrite, Schedule, Timer, MyPage, and Profile use relative paths
and should be switched to the project alias; update each lazy(() =>
import('./pages/...')) to use the alias form lazy(() => import('@/pages/...'))
so all imports match the alias-based convention and avoid breakage during
refactors.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2881528b-3ee8-4684-8c6f-2a36ea64ab4e
📒 Files selected for processing (3)
src/App.tsxsrc/components/common/RouteLoadingFallback.tsxsrc/components/layout/index.tsx
| <div | ||
| role="status" | ||
| aria-live="polite" | ||
| className={cn( | ||
| 'text-body3 flex w-full items-center justify-center gap-3 text-indigo-400', | ||
| fullScreen ? 'h-(--viewport-height)' : 'min-h-full flex-1 py-10' | ||
| )} | ||
| > | ||
| <div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-200 border-t-indigo-600" /> | ||
| </div> |
There was a problem hiding this comment.
로딩 상태가 스크린리더에 전달되지 않습니다.
role="status"는 들어갔지만 읽을 텍스트가 없어서 보조기기에는 상태 변화가 거의 전달되지 않습니다. sr-only 문구를 넣고 스피너는 aria-hidden="true"로 숨겨 주세요.
♿ 제안 수정
<div
role="status"
aria-live="polite"
className={cn(
'text-body3 flex w-full items-center justify-center gap-3 text-indigo-400',
fullScreen ? 'h-(--viewport-height)' : 'min-h-full flex-1 py-10'
)}
>
- <div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-200 border-t-indigo-600" />
+ <div
+ aria-hidden="true"
+ className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-200 border-t-indigo-600"
+ />
+ <span className="sr-only">페이지를 불러오는 중</span>
</div>As per coding guidelines, 접근성(aria-*, role, 키보드 탐색)이 적절히 처리되어야 합니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div | |
| role="status" | |
| aria-live="polite" | |
| className={cn( | |
| 'text-body3 flex w-full items-center justify-center gap-3 text-indigo-400', | |
| fullScreen ? 'h-(--viewport-height)' : 'min-h-full flex-1 py-10' | |
| )} | |
| > | |
| <div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-200 border-t-indigo-600" /> | |
| </div> | |
| <div | |
| role="status" | |
| aria-live="polite" | |
| className={cn( | |
| 'text-body3 flex w-full items-center justify-center gap-3 text-indigo-400', | |
| fullScreen ? 'h-(--viewport-height)' : 'min-h-full flex-1 py-10' | |
| )} | |
| > | |
| <div | |
| aria-hidden="true" | |
| className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-200 border-t-indigo-600" | |
| /> | |
| <span className="sr-only">페이지를 불러오는 중</span> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/common/RouteLoadingFallback.tsx` around lines 9 - 18, The
loading markup in RouteLoadingFallback lacks readable text for screen readers;
update the component (RouteLoadingFallback) so the visible spinner div has
aria-hidden="true" (e.g., the element with className "h-8 w-8 animate-spin ...")
and add a visually-hidden / sr-only text node inside the role="status" container
(e.g., "Loading…" or "로딩 중…") so screen readers announce the status while the
spinner is hidden from assistive tech.
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
src/pages/Home/components/InfiniteClubCarousel.tsx (1)
58-59: 같은 우선순위 계산을 한 번만 수행하면 가독성이 좋아집니다.같은
isPriorityImage(...)호출이 중복되어 있어 한 번 계산한 값을 재사용하는 편이 더 명확합니다.리팩터링 예시
{displayClubs.map(({ club, key }, index) => { const isDuplicate = shouldLoop && (index < clubs.length || index >= clubs.length * 2); + const shouldPrioritizeImage = isPriorityImage(index, clubs.length, shouldLoop); return ( <div key={key} className={cn('shrink-0 py-1', shouldCenterCard ? 'snap-center' : 'snap-start px-[3px]')} style={{ width: `${CLUB_CARD_WIDTH}px` }} > <RecommendedClubCard club={club} className="w-full" ariaHidden={isDuplicate} - imageFetchPriority={isPriorityImage(index, clubs.length, shouldLoop) ? 'auto' : 'low'} - imageLoading={isPriorityImage(index, clubs.length, shouldLoop) ? 'eager' : 'lazy'} + imageFetchPriority={shouldPrioritizeImage ? 'auto' : 'low'} + imageLoading={shouldPrioritizeImage ? 'eager' : 'lazy'} tabIndex={isDuplicate ? -1 : 0} /> </div> ); })}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Home/components/InfiniteClubCarousel.tsx` around lines 58 - 59, Compute the result of isPriorityImage(index, clubs.length, shouldLoop) once and reuse it for both props to improve readability: call isPriorityImage(...) into a local const (e.g., isPriority) near where index is in scope, then use that boolean to set imageFetchPriority ('auto'/'low') and imageLoading ('eager'/'lazy') instead of calling isPriorityImage twice; update the component that renders the image (the element assigning imageFetchPriority and imageLoading) to reference this const.src/stores/authStore.ts (1)
34-64:hydrateUser를initialize외부로 분리하면 가독성이 향상됩니다.현재
hydrateUser가initialize내부에 정의되어 있어 매 호출마다 새 클로저가 생성됩니다. 모듈 레벨hydrateUserPromise로 중복 호출은 방지되지만, 스토어 외부 또는 상단에 분리하면 구조가 더 명확해집니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/stores/authStore.ts` around lines 34 - 64, The hydrateUser function is currently defined inside initialize causing a new closure on each call; extract hydrateUser to module scope (outside initialize) while keeping the shared hydrateUserPromise variable at module level, and update references to getMyInfo(), get(), set(), and window.ReactNativeWebView so behavior is identical; ensure hydrateUser still checks get().accessToken against the passed nextAccessToken, posts the LOGIN_COMPLETE message, handles bridge errors, and resets hydrateUserPromise in finally, and update initialize to call the newly moved hydrateUser(nextAccessToken).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 10: Update package.json to use cross-env for cross-platform environment
variables by adding cross-env to devDependencies and changing the
"build:analyze:staging" script to prefix ANALYZE=true with cross-env (i.e.,
"cross-env ANALYZE=true ..."); ensure you add a .env.example at project root
containing VITE_API_PATH (e.g., VITE_API_PATH=http://localhost:3000/api) because
src/apis/client.ts expects VITE_API_PATH; also add or document a .env.staging
file (or ensure .env.staging is created) for the --mode staging build so the
build has the required staging env vars.
In `@src/components/layout/Header/components/InfoHeader.tsx`:
- Around line 13-18: The component InfoHeader.tsx is using a non-existent token
`bg-indigo-10`; add the missing token and switch the usage: define
`--color-indigo-5: `#f4f6f9`` in your theme.css and replace `bg-indigo-10` in
InfoHeader.tsx with `bg-indigo-5` (or update any className that references
`bg-indigo-10`) so the background color uses the newly defined
`--color-indigo-5` token.
- Around line 22-23: In InfoHeader.tsx, the second skeleton div uses a
non-existent CSS token "bg-indigo-10"; replace that class with a defined token
(e.g., "bg-indigo-5" or another valid token in theme.css such as "bg-indigo-25")
so the element uses an existing color variable—update the className string on
the element containing "bg-indigo-10 h-4 w-36 animate-pulse rounded" to use the
valid token.
In `@src/pages/Chat/hooks/useUnreadChatCount.ts`:
- Around line 37-43: The hook useUnreadChatCount.ts uses useQuery with
chatQueryKeys.rooms() but sets a different staleTime/refetchInterval than the
existing hook useChat.ts, which can cause unexpected cache behavior; update
useUnreadChatCount.ts so its useQuery options (staleTime and refetchInterval)
match the ones used by useChat.ts, or change the query key to a dedicated one
(e.g., chatQueryKeys.unreadRooms()) and keep distinct timings—locate the
useQuery call in useUnreadChatCount.ts and either copy the exact option values
from useChat.ts or replace chatQueryKeys.rooms() with a unique key to avoid
option collisions.
In `@src/pages/Home/components/InfiniteClubCarousel.tsx`:
- Around line 12-20: isPriorityImage currently only prioritizes images around
the computed middleSegmentStartIndex when shouldLoop is true, which can miss the
actual initially visible cards before scroll correction; update isPriorityImage
to also include the initial visible range so first-paint images get high
priority. Modify the function (isPriorityImage, parameters index, clubsLength,
shouldLoop and local middleSegmentStartIndex) to compute a visible window (e.g.,
include indices < 2 or a configurable visibleCount) and return true if index
falls within either the looped middleSegmentStartIndex ± N OR the initial
visible range (index < visibleCount), ensuring the initial on-screen cards are
marked priority on first render.
In `@src/pages/Home/hooks/useGetHomeCouncilNotices.ts`:
- Line 3: The Home hook currently imports councilQueryKeys from the
CouncilDetail page hook (useGetCouncilInfo), creating a route-to-route
dependency; extract councilQueryKeys into a shared domain module (e.g., create a
new module named something like src/apis/council/queryKeys.ts) and export the
same symbol (councilQueryKeys) there, then update useGetHomeCouncilNotices.ts
and the CouncilDetail hook (useGetCouncilInfo) to import councilQueryKeys from
that new shared module instead of from the page hook to avoid chunk coupling.
In `@src/stores/authStore.ts`:
- Around line 29-32: The initialization currently sets isAuthenticated based
solely on user (the user variable) which can mark the app authenticated even if
accessToken is expired; update the logic in the auth initialization (where set({
isAuthenticated: true, isLoading: false }) is called) to validate the token or
attempt a refresh before marking authenticated: check the stored accessToken
expiry (or call an existing refresh method such as
refreshAccessToken/refreshToken or an apiClient.refresh flow) and only set
isAuthenticated true after a successful validation/refresh, otherwise clear
user/auth state and set isAuthenticated false; reference the user variable, the
accessToken/refreshToken storage, and the set(...) call to locate where to
implement this change.
---
Nitpick comments:
In `@src/pages/Home/components/InfiniteClubCarousel.tsx`:
- Around line 58-59: Compute the result of isPriorityImage(index, clubs.length,
shouldLoop) once and reuse it for both props to improve readability: call
isPriorityImage(...) into a local const (e.g., isPriority) near where index is
in scope, then use that boolean to set imageFetchPriority ('auto'/'low') and
imageLoading ('eager'/'lazy') instead of calling isPriorityImage twice; update
the component that renders the image (the element assigning imageFetchPriority
and imageLoading) to reference this const.
In `@src/stores/authStore.ts`:
- Around line 34-64: The hydrateUser function is currently defined inside
initialize causing a new closure on each call; extract hydrateUser to module
scope (outside initialize) while keeping the shared hydrateUserPromise variable
at module level, and update references to getMyInfo(), get(), set(), and
window.ReactNativeWebView so behavior is identical; ensure hydrateUser still
checks get().accessToken against the passed nextAccessToken, posts the
LOGIN_COMPLETE message, handles bridge errors, and resets hydrateUserPromise in
finally, and update initialize to call the newly moved
hydrateUser(nextAccessToken).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f66597d5-2e75-4366-bba5-237f7dde2ad4
⛔ Files ignored due to path filters (4)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!pnpm-lock.yamland included by**src/assets/image/chat-cat-header.pngis excluded by!**/*.png,!src/assets/**and included by**src/assets/image/chat-cat-login.pngis excluded by!**/*.png,!src/assets/**and included by**src/assets/svg/chat-cat.svgis excluded by!**/*.svg,!src/assets/**and included by**
📒 Files selected for processing (13)
package.jsonsrc/components/auth/AuthGuard.tsxsrc/components/layout/Header/components/InfoHeader.tsxsrc/components/layout/Header/components/NotificationBell.tsxsrc/pages/Auth/Login/index.tsxsrc/pages/Chat/hooks/useUnreadChatCount.tssrc/pages/Council/CouncilDetail/hooks/useGetCouncilInfo.tssrc/pages/Home/components/CouncilNoticeSection.tsxsrc/pages/Home/components/InfiniteClubCarousel.tsxsrc/pages/Home/components/RecommendedClubCard.tsxsrc/pages/Home/hooks/useGetHomeCouncilNotices.tssrc/stores/authStore.tsvite.config.ts
✅ Files skipped from review due to trivial changes (1)
- src/pages/Council/CouncilDetail/hooks/useGetCouncilInfo.ts
| "scripts": { | ||
| "start": "vite", | ||
| "build": "tsc -b && vite build", | ||
| "build:analyze:staging": "ANALYZE=true tsc -b && ANALYZE=true vite build --mode staging", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# .env.staging 파일 존재 여부 확인
fd -t f '.env.staging' --max-depth 1
# 또는 .env 관련 파일 목록 확인
ls -la .env* 2>/dev/null || echo "No .env files found in root"Repository: BCSDLab/KONECT_FRONT_END
Length of output: 95
🏁 Script executed:
# 1. .env.example 파일 확인
fd -t f '.env.example'
# 2. package.json에서 cross-env 존재 여부 확인
grep -E '"cross-env"|dependencies|devDependencies' package.json | head -20
# 3. src/apis/client.ts에서 VITE_API_PATH 사용 확인
fd -t f 'client.ts' src/apis/ && cat src/apis/client.ts | grep -A 5 -B 5 'VITE_API_PATH'Repository: BCSDLab/KONECT_FRONT_END
Length of output: 507
🏁 Script executed:
# .env.example 파일 내용 확인
cat .env.example
# package.json의 전체 scripts와 devDependencies 확인
cat package.json | head -60Repository: BCSDLab/KONECT_FRONT_END
Length of output: 1763
크로스 플랫폼 호환성을 위해 cross-env 사용 필수
ANALYZE=true 문법은 Windows에서 동작하지 않습니다. cross-env 패키지를 devDependencies에 추가하고 적용해야 합니다.
또한 --mode staging 사용 시 필요한 .env.staging 파일이 없고, 더 중요하게는 src/apis/client.ts에서 필수로 요구하는 VITE_API_PATH 환경변수를 정의할 .env.example 파일이 존재하지 않습니다.
♻️ 수정 예시
1. cross-env 적용
- "build:analyze:staging": "ANALYZE=true tsc -b && ANALYZE=true vite build --mode staging",
+ "build:analyze:staging": "cross-env ANALYZE=true tsc -b && cross-env ANALYZE=true vite build --mode staging",2. devDependencies에 cross-env 추가
+ "cross-env": "^7.0.3",3. .env.example 생성 (프로젝트 루트)
VITE_API_PATH=http://localhost:3000/api
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 10, Update package.json to use cross-env for
cross-platform environment variables by adding cross-env to devDependencies and
changing the "build:analyze:staging" script to prefix ANALYZE=true with
cross-env (i.e., "cross-env ANALYZE=true ..."); ensure you add a .env.example at
project root containing VITE_API_PATH (e.g.,
VITE_API_PATH=http://localhost:3000/api) because src/apis/client.ts expects
VITE_API_PATH; also add or document a .env.staging file (or ensure .env.staging
is created) for the --mode staging build so the build has the required staging
env vars.
| const { data } = useQuery({ | ||
| queryKey: chatQueryKeys.rooms(), | ||
| queryFn: getChatRooms, | ||
| enabled: isEnabled, | ||
| staleTime: UNREAD_CHAT_COUNT_REFETCH_INTERVAL, | ||
| refetchInterval: isEnabled ? UNREAD_CHAT_COUNT_REFETCH_INTERVAL : false, | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# useChat.ts의 쿼리 설정 확인
ast-grep --pattern $'useQuery({
queryKey: chatQueryKeys.rooms(),
$$$
})'Repository: BCSDLab/KONECT_FRONT_END
Length of output: 641
🏁 Script executed:
fd -type f -name "*useChat*" src/Repository: BCSDLab/KONECT_FRONT_END
Length of output: 238
🏁 Script executed:
rg "chatQueryKeys\.rooms\(\)" src/ -A 5 -B 2Repository: BCSDLab/KONECT_FRONT_END
Length of output: 2409
🏁 Script executed:
cat -n src/pages/Chat/hooks/useUnreadChatCount.ts | head -50Repository: BCSDLab/KONECT_FRONT_END
Length of output: 1683
동일 쿼리 키의 옵션 불일치로 인한 캐시 동작 확인 필요
useChat.ts와 동일한 chatQueryKeys.rooms()을 사용하면서 staleTime 설정이 다릅니다. 두 훅이 동시에 활성화될 때 캐시 동작이 예기치 않게 동작할 수 있으니, 쿼리 옵션을 통일하거나 별도의 쿼리 키 사용을 검토해주세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/Chat/hooks/useUnreadChatCount.ts` around lines 37 - 43, The hook
useUnreadChatCount.ts uses useQuery with chatQueryKeys.rooms() but sets a
different staleTime/refetchInterval than the existing hook useChat.ts, which can
cause unexpected cache behavior; update useUnreadChatCount.ts so its useQuery
options (staleTime and refetchInterval) match the ones used by useChat.ts, or
change the query key to a dedicated one (e.g., chatQueryKeys.unreadRooms()) and
keep distinct timings—locate the useQuery call in useUnreadChatCount.ts and
either copy the exact option values from useChat.ts or replace
chatQueryKeys.rooms() with a unique key to avoid option collisions.
| @@ -0,0 +1,14 @@ | |||
| import { useSuspenseQuery } from '@tanstack/react-query'; | |||
| import { getCouncilNotice } from '@/apis/council'; | |||
| import { councilQueryKeys } from '@/pages/Council/CouncilDetail/hooks/useGetCouncilInfo'; | |||
There was a problem hiding this comment.
라우트 간 쿼리키 의존을 분리해주세요.
Line 3처럼 Home 훅이 CouncilDetail 페이지 훅 파일을 직접 참조하면, 라우트 분할 시 청크 결합이 생겨 성능 이점이 줄 수 있습니다. councilQueryKeys를 페이지 경로가 아닌 공유 도메인 모듈(예: @/apis/council/queryKeys)로 분리해 양쪽에서 공통 import 하세요.
예시 변경안
- import { councilQueryKeys } from '@/pages/Council/CouncilDetail/hooks/useGetCouncilInfo';
+ import { councilQueryKeys } from '@/apis/council/queryKeys';// src/apis/council/queryKeys.ts
export const councilQueryKeys = {
all: ['council'] as const,
noticesPreview: (limit: number) => [...councilQueryKeys.all, 'noticesPreview', limit] as const,
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/Home/hooks/useGetHomeCouncilNotices.ts` at line 3, The Home hook
currently imports councilQueryKeys from the CouncilDetail page hook
(useGetCouncilInfo), creating a route-to-route dependency; extract
councilQueryKeys into a shared domain module (e.g., create a new module named
something like src/apis/council/queryKeys.ts) and export the same symbol
(councilQueryKeys) there, then update useGetHomeCouncilNotices.ts and the
CouncilDetail hook (useGetCouncilInfo) to import councilQueryKeys from that new
shared module instead of from the page hook to avoid chunk coupling.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/pages/Home/components/InfiniteClubCarousel.tsx (1)
66-67: LCP 최적화를 위해fetchPriority="high"고려해 볼 수 있습니다.현재
'auto'도 동작하지만, 우선 로드가 필요한 이미지라면'high'가 브라우저에게 더 명시적인 힌트를 줍니다.선택적 수정 제안
- imageFetchPriority={isPriority ? 'auto' : 'low'} + imageFetchPriority={isPriority ? 'high' : 'low'}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Home/components/InfiniteClubCarousel.tsx` around lines 66 - 67, For LCP optimization, change the image fetch priority when isPriority is true from 'auto' to 'high' by updating the imageFetchPriority prop used in InfiniteClubCarousel (where imageFetchPriority currently uses isPriority ? 'auto' : 'low'); keep the fallback 'low' for non-priority images and leave imageLoading (isPriority ? 'eager' : 'lazy') as-is so high-priority images get both 'high' fetch priority and eager loading.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/pages/Home/components/InfiniteClubCarousel.tsx`:
- Around line 66-67: For LCP optimization, change the image fetch priority when
isPriority is true from 'auto' to 'high' by updating the imageFetchPriority prop
used in InfiniteClubCarousel (where imageFetchPriority currently uses isPriority
? 'auto' : 'low'); keep the fallback 'low' for non-priority images and leave
imageLoading (isPriority ? 'eager' : 'lazy') as-is so high-priority images get
both 'high' fetch priority and eager loading.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e2ab5b5e-78b1-4b08-95dd-454acac88d34
📒 Files selected for processing (4)
src/components/layout/Header/components/InfoHeader.tsxsrc/components/layout/index.tsxsrc/pages/Home/components/InfiniteClubCarousel.tsxsrc/stores/authStore.ts
✅ Files skipped from review due to trivial changes (1)
- src/components/layout/Header/components/InfoHeader.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/stores/authStore.ts
하나의 chunk로 묶여있던 페이지 코드들을 lazy import로 분리합니다
Summary by CodeRabbit
릴리스 노트
새로운 기능
성능 개선
UI 개선
버그 수정