Skip to content

refactor: 찜한 기사님 조회·캐시 및 권한 구조 개선 - #54

Merged
yooseohyeon merged 14 commits into
devfrom
refactor/mover-favorite
Aug 3, 2026
Merged

yooseohyeon merged 14 commits into
devfrom
refactor/mover-favorite

Conversation

@yooseohyeon

@yooseohyeon yooseohyeon commented Aug 1, 2026

Copy link
Copy Markdown
Member

📋 작업 내용

찜 목록 페이지를 CUSTOMER 보호 라우트로 이동하고, 사용자별 캐시 및 고객 전용 찜 mutation의 권한 검증을 보완했습니다.
또한 찜한 기사님 페이지와 기사님 찾기 페이지에서의 찜한 기사님 사이드바에 사용하는 조회·인증·캐시 로직을 정리했습니다.


🔥 변경 사항

찜 목록 조회 로직 정리

  • 찜 목록의 인증 상태, Query Key scope, 쿼리 실행 조건을 useFavoriteMovers.ts에서 공통으로 관리하도록 변경
  • API 응답을 화면용 기사님 모델로 변환하는 로직을 useFavoriteMovers.ts로 이동 (FavoriteMoversSidebar.tsx, FavoriteMoversContent.tsx 등 컴포넌트에서 hook으로 이동)
  • 인증 대기와 초기 조회 상태를 isInitialLoading으로 통합
  • 사이드바가 인증 Store 구조 대신 isCustomerLoggedIn, shouldHideForMover처럼 의미가 명확한 상태를 사용하도록 정리 (FavoriteMoversSidebar.tsx)

사용자별 캐시 정합성 보완

  • 찜 mutation 완료 후 기사님 목록·상세·찜 목록의 invalidation 범위를 현재 사용자 scope로 제한 (favoriteMoverCache.ts)
  • 찜 목록 캐시 유틸(favoriteMoverCache.ts)에서 unknown 반환과 호출부의 강제 타입 단언 제거
  • queryKeys.ts에서 현재 사용자의 기사님 상세 캐시 prefix를 생성하는 getMoverDetailScopeQueryKey() 추가

선택 상태 로직 개선

  • useFavoriteMoversSelection.ts에서 선택 및 제외 ID를 Set으로 변환해 반복적인 배열 탐색을 줄임
    • 선택 개수 계산, 최대 선택 제한 및 카드 선택 여부 판별에서 배열 includes() 대신 Set.has()를 사용하도록 변경
    • 기존 개별 선택, 전체 선택 및 일부 제외 동작은 유지

고객 전용 찜 권한 보완

  • CUSTOMER 계정만 찜 mutation을 실행하도록 검증 추가 (useFavoriteMover.ts, useBulkRemoveFavoriteMovers.ts)
  • 비회원이 찜 버튼을 누르면 기존과 동일하게 로그인 안내 표시
  • MOVER·ADMIN·역할 미확정 사용자의 찜 API 요청을 Hook에서 차단
  • MOVER가 기사님 찾기 목록에 접근한 경우 하트 버튼을 읽기 전용으로 표시하도록 변경

보호 라우트 및 컴포넌트 구조 정리

  • /movers/favorites 페이지를 (customer)/(protected) 아래로 이동
  • 중복된 CustomerAuthGate 래퍼 제거
  • RoleGuard에 로딩 fallback을 추가해 인증 확인 중 기존 찜 목록 스켈레톤을 그대로 표시하도록 처리
  • 찜 관련 컴포넌트를 components/mover/favorites 폴더로 이동

✅ 체크리스트

  • 로컬에서 정상 동작을 확인했습니다.
  • 기존 기능에 영향을 주지 않는지 확인했습니다.
  • 불필요한 console.log를 제거했습니다.
  • lint를 통과했습니다.
  • README 또는 문서를 수정했습니다. (필요 시)
  • API 명세와 일치하는지 확인했습니다.

📷 스크린샷 (선택)

UI 디자인 변경 사항이 없어 생략했습니다.


💬 To Reviewer

  • 찜 목록 훅에서 인증 상태, 데이터 가공 및 쿼리 실행 조건을 함께 관리하도록 변경했습니다. 해당 책임 분리가 적절한지 확인 부탁드립니다.
  • /movers/favorites는 CUSTOMER protected route로 이동했지만, 공개 페이지인 /movers에는 MOVER도 접근할 수 있어 MoverCard의 찜 버튼을 읽기 전용으로 처리했습니다. 비회원·CUSTOMER·MOVER별 버튼 노출과 mutation 실행 조건이 적절한지 확인 부탁드립니다.
  • useFavoriteMover가 기사님 찾기뿐 아니라 견적 카드와 상세에서도 재사용되고 있어 Hook에도 CUSTOMER 검증을 추가했습니다. 추후 MOVER의 /movers 접근을 제한하더라도 이를 최종 방어선으로 유지할지, RoleGuard와 중복된 검증으로 볼지 의견 부탁드립니다.
  • 단건·일괄 찜 mutation의 cache invalidation을 현재 사용자 scope로 제한했습니다. 사용자별 Query Key와 함께 계정 전환 시 캐시 혼합을 충분히 방지하는지 중점적으로 확인 부탁드립니다.
  • /movers/favorites의 protected route 이동과 인증 대기 중 기존 스켈레톤을 표시하기 위해 추가한 RoleGuardloadingFallback 방식이 적절한지 의견 부탁드립니다.

Summary by CodeRabbit

  • 새 기능

    • 즐겨찾기 기사 목록에 전체 선택 및 일괄 삭제 기능을 추가했습니다.
    • 즐겨찾기 상태를 변경할 수 없는 경우 읽기 전용 버튼으로 표시합니다.
    • 인증 및 권한 상태에 맞춰 즐겨찾기 콘텐츠와 로딩 화면을 제공합니다.
  • 버그 수정

    • 인증 전환 시 즐겨찾기 목록과 기사 상세 정보가 올바르게 갱신됩니다.
    • 즐겨찾기 목록의 초기 로딩 및 비로그인 상태 처리를 개선했습니다.
    • 기사 찜 버튼의 접근성 라벨을 개선했습니다.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0263e22d-b524-4579-8f32-76afcb3e30ca

📥 Commits

Reviewing files that changed from the base of the PR and between f807a84 and 2063576.

📒 Files selected for processing (22)
  • src/app/(customer)/(protected)/layout.tsx
  • src/app/(customer)/(protected)/movers/favorites/page.tsx
  • src/components/auth/LoginRequiredModal.tsx
  • src/components/auth/RoleGuard.tsx
  • src/components/estimate/DesignateSuccessModal.tsx
  • src/components/estimate/EstimateRequestRequiredModal.tsx
  • src/components/mover/FavoriteMoversPageClient.tsx
  • src/components/mover/MoverCard.tsx
  • src/components/mover/detail/LikeOutlineButton.tsx
  • src/components/mover/favorites/FavoriteMoversContent.tsx
  • src/components/mover/favorites/FavoriteMoversDeleteConfirmModal.tsx
  • src/components/mover/favorites/FavoriteMoversLoadingSkeleton.tsx
  • src/components/mover/favorites/FavoriteMoversSidebar.tsx
  • src/components/mover/favorites/FavoriteMoversToolbar.tsx
  • src/components/mover/list/MoversPageView.tsx
  • src/hooks/useBulkRemoveFavoriteMovers.ts
  • src/hooks/useFavoriteMover.ts
  • src/hooks/useFavoriteMovers.ts
  • src/hooks/useFavoriteMoversSelection.ts
  • src/lib/constants/queryKeys.ts
  • src/lib/kakao/shareTemplate.ts
  • src/lib/utils/favoriteMoverCache.ts

📝 Walkthrough

Walkthrough

고객 인증 준비 상태를 즐겨찾기 조회와 변경 흐름에 적용했습니다. RoleGuard는 인증 중 fallback을 렌더링합니다. 즐겨찾기 훅은 authScope별 쿼리와 캐시를 사용합니다. 비고객은 찜 변경을 수행할 수 없습니다.

Changes

즐겨찾기 기사 흐름

Layer / File(s) Summary
인증 로딩과 페이지 연결
src/app/(customer)/(protected)/layout.tsx, src/app/(customer)/(protected)/movers/favorites/page.tsx, src/components/auth/RoleGuard.tsx
RoleGuard가 인증 확인 중 loadingFallback을 렌더링합니다. 즐겨찾기 페이지는 FavoriteMoversContent를 직접 렌더링합니다.
인증 범위 조회와 캐시 갱신
src/hooks/useFavoriteMovers.ts, src/components/mover/favorites/*, src/lib/constants/queryKeys.ts, src/lib/utils/favoriteMoverCache.ts
즐겨찾기 조회 훅이 고객 인증 상태를 확인합니다. 훅은 movers, query, totalCount, 로딩 상태를 반환합니다. 캐시 무효화는 authScope별로 수행합니다.
찜 변경 권한과 읽기 전용 카드
src/hooks/useFavoriteMover.ts, src/hooks/useBulkRemoveFavoriteMovers.ts, src/components/mover/MoverCard.tsx
찜 변경과 일괄 해제는 인증 준비 상태와 CUSTOMER 역할을 확인합니다. 변경할 수 없는 경우 MoverCard는 비대화형 아이콘을 렌더링합니다.
선택과 일괄 해제 UI
src/components/mover/favorites/FavoriteMoversToolbar.tsx, src/hooks/useFavoriteMoversSelection.ts, src/components/mover/favorites/FavoriteMoversDeleteConfirmModal.tsx
전체 선택, 선택 개수, 일괄 해제 버튼을 추가했습니다. 선택 판별은 메모이제이션된 Set을 사용합니다.
관련 표시와 공유 동작 정리
src/components/auth/LoginRequiredModal.tsx, src/components/estimate/*, src/components/mover/detail/LikeOutlineButton.tsx, src/lib/kakao/shareTemplate.ts
패널 클래스 조합을 cn으로 변경했습니다. 찜 버튼 접근성 라벨과 Kakao 공유 템플릿 ID fallback을 변경했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CustomerProtectedLayout
  participant RoleGuard
  participant useFavoriteMoversInfinite
  participant QueryClient
  CustomerProtectedLayout->>RoleGuard: loadingFallback 전달
  RoleGuard-->>CustomerProtectedLayout: 인증 확인 중 fallback 렌더링
  RoleGuard->>useFavoriteMoversInfinite: CUSTOMER 인증 후 조회
  useFavoriteMoversInfinite->>QueryClient: authScope별 즐겨찾기 쿼리 요청
  QueryClient-->>useFavoriteMoversInfinite: 페이지 데이터와 totalCount 반환
  useFavoriteMoversInfinite-->>RoleGuard: movers와 조회 상태 반환
Loading

Possibly related PRs

Suggested reviewers: 9g-g9

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 찜 목록 조회, 캐시, 권한 구조 개선이라는 PR의 주요 변경 사항을 구체적으로 요약합니다.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/mover-favorite

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

🧹 Nitpick comments (1)
src/components/mover/favorites/FavoriteMoversDeleteConfirmModal.tsx (1)

5-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

클래스 조합에 cn()을 사용하세요.

Array.join(" ") 대신 프로젝트 공통 cn()을 사용하세요. 이 방식은 다른 컴포넌트의 클래스 병합 방식과 일치합니다.

수정 예시
 import Modal from "`@/components/common/Modal/Modal`";
+import { cn } from "`@/lib/utils/cn`";

-const PANEL_CLASSNAME = [
+const PANEL_CLASSNAME = cn(
   "items-stretch text-left",
   "rounded-24 md:rounded-32",
   "w-full max-w-[292px] gap-30 px-16 py-24",
   "md:max-w-[480px] md:gap-40 md:px-24 md:pt-32 md:pb-40",
-].join(" ");
+);

As per path instructions, “클래스 조합은 src/lib/utils/cn.tscn()을 사용합니다.”

🤖 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/components/mover/favorites/FavoriteMoversDeleteConfirmModal.tsx` around
lines 5 - 10, Update the PANEL_CLASSNAME definition in
FavoriteMoversDeleteConfirmModal to compose its Tailwind classes with the shared
cn() utility from src/lib/utils/cn.ts instead of Array.join(" "), preserving the
existing class values and ordering.

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.

Inline comments:
In `@src/components/mover/MoverCard.tsx`:
- Around line 76-95: Update the aria-label on the toggle button in MoverCard so
it reflects the next action: announce “찜하기” when isFavorite is false and “찜 취소”
when isFavorite is true, matching the onToggle(!isFavorite) behavior.

---

Nitpick comments:
In `@src/components/mover/favorites/FavoriteMoversDeleteConfirmModal.tsx`:
- Around line 5-10: Update the PANEL_CLASSNAME definition in
FavoriteMoversDeleteConfirmModal to compose its Tailwind classes with the shared
cn() utility from src/lib/utils/cn.ts instead of Array.join(" "), preserving the
existing class values and ordering.
🪄 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: 54c25792-998f-4175-bc36-a4f7b5d4104c

📥 Commits

Reviewing files that changed from the base of the PR and between e4d18e1 and 53548a6.

📒 Files selected for processing (17)
  • src/app/(customer)/(protected)/layout.tsx
  • src/app/(customer)/(protected)/movers/favorites/page.tsx
  • src/components/auth/RoleGuard.tsx
  • src/components/mover/FavoriteMoversPageClient.tsx
  • src/components/mover/MoverCard.tsx
  • src/components/mover/MoversPageView.tsx
  • src/components/mover/favorites/FavoriteMoversContent.tsx
  • src/components/mover/favorites/FavoriteMoversDeleteConfirmModal.tsx
  • src/components/mover/favorites/FavoriteMoversLoadingSkeleton.tsx
  • src/components/mover/favorites/FavoriteMoversSidebar.tsx
  • src/components/mover/favorites/FavoriteMoversToolbar.tsx
  • src/hooks/useBulkRemoveFavoriteMovers.ts
  • src/hooks/useFavoriteMover.ts
  • src/hooks/useFavoriteMovers.ts
  • src/hooks/useFavoriteMoversSelection.ts
  • src/lib/constants/queryKeys.ts
  • src/lib/utils/favoriteMoverCache.ts
💤 Files with no reviewable changes (1)
  • src/components/mover/FavoriteMoversPageClient.tsx

Comment thread src/components/mover/MoverCard.tsx

@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: 1

🤖 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/components/mover/detail/LikeOutlineButton.tsx`:
- Around line 44-52: Update LikeOutlineButton’s aria-label to include the
state-dependent action by reusing the existing label value derived from
isFavorite, rather than the static accessibleLabel text. Preserve moverName in
the accessible label when present while ensuring favorite and unfavorite states
announce their respective actions.
🪄 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: 0607f05a-ad81-47b8-8697-4bd512d5bf49

📥 Commits

Reviewing files that changed from the base of the PR and between 53548a6 and 891e9ee.

📒 Files selected for processing (7)
  • src/components/auth/LoginRequiredModal.tsx
  • src/components/estimate/DesignateSuccessModal.tsx
  • src/components/estimate/EstimateRequestRequiredModal.tsx
  • src/components/mover/MoverCard.tsx
  • src/components/mover/detail/LikeOutlineButton.tsx
  • src/components/mover/favorites/FavoriteMoversDeleteConfirmModal.tsx
  • src/lib/kakao/shareTemplate.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/mover/MoverCard.tsx
  • src/components/mover/favorites/FavoriteMoversDeleteConfirmModal.tsx

Comment thread src/components/mover/detail/LikeOutlineButton.tsx Outdated

@juengseulki juengseulki left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

📋 PR 리뷰

👍 좋았던 점

  • 찜 목록의 인증 상태, 사용자별 Query Scope, 쿼리 실행 조건과 응답 데이터 가공을 useFavoriteMovers로 모아 컴포넌트의 책임을 줄인 점이 좋았습니다.
  • FavoriteMoversSidebarFavoriteMoversContent가 Zustand 인증 구조나 API 페이지 응답을 직접 처리하지 않고, 화면에 필요한 의미 있는 상태만 사용하도록 정리되어 가독성이 좋아졌습니다.
  • 기사 목록·상세·찜 목록의 Query Key를 user:${userId} 기준으로 분리해 고객별 캐시가 서로 섞이지 않도록 보완한 점이 좋았습니다.
  • 사용자 식별이 완료되지 않은 인증 상태에서는 사용자별 Query를 실행하지 않도록 해 인증 복구 과정에서 잘못된 Scope의 캐시가 생성되는 것을 방지했습니다.
  • 단건·일괄 찜 Mutation의 낙관적 업데이트와 Invalidation 범위를 현재 사용자 Scope로 제한해 다른 고객이나 Guest 캐시까지 불필요하게 수정하지 않도록 정리했습니다.
  • 캐시 유틸에서 일반 목록과 Infinite Query 데이터를 명시적인 Union과 Type Guard로 구분해, 기존의 unknown 반환과 호출부 타입 단언을 제거한 점이 좋았습니다.
  • useFavoriteMoveruseBulkRemoveFavoriteMovers 모두 CUSTOMER 역할을 다시 확인하도록 보완해, 여러 화면에서 재사용되더라도 잘못된 권한으로 Mutation이 실행되지 않도록 했습니다.
  • /movers/favorites를 CUSTOMER Protected Route로 이동하고 기존 CustomerAuthGate를 제거해 접근 제어 책임을 RoleGuard로 통일한 점이 좋았습니다.
  • 인증 확인 중에는 빈 화면 대신 기존 찜 목록 스켈레톤을 표시하도록 RoleGuardloadingFallback을 지원한 점도 자연스러웠습니다.
  • 선택·제외 ID를 Set으로 변환해 선택 여부와 제한 검사를 Set.has()로 처리하면서 반복적인 배열 탐색을 줄인 점도 좋았습니다.

🚨 수정이 필요한 부분

  • useFavoriteMoversQueryContext()에서 찜 사이드바를 숨기는 조건이 MOVER에만 한정되어 있습니다.
shouldHideForMover:
  !auth.isPending && auth.isAuthenticated && auth.isMover,

이 상태에서 ADMIN이 공개 페이지인 /movers에 접근하면 로그인한 사용자임에도 isCustomerLoggedInshouldHideForMover가 모두 false가 되어, 사이드바에 비회원용 로그인 안내가 표시될 수 있습니다.

찜 목록이 CUSTOMER 전용 기능이라면 MOVER뿐 아니라 로그인한 모든 비고객 사용자에게 사이드바를 숨기는 조건으로 일반화하는 편이 자연스러워 보입니다.

shouldHideForNonCustomer:
  !auth.isPending &&
  auth.isAuthenticated &&
  !isCustomerLoggedIn

또는 ADMIN에게 별도 안내를 제공하려는 정책이라면 비회원 상태와 구분된 UI가 필요해 보입니다.

전체적으로 인증·캐시·권한 책임을 꼼꼼하게 정리한 리팩토링이었습니다. ADMIN의 찜 사이드바 상태 분기만 보완되면 병합해도 좋을 것 같습니다. 수고하셨습니다! 😊


💬 To Reviewer 질문에 대한 답변

1. useFavoriteMovers가 인증 상태·데이터 가공·쿼리 조건을 함께 관리하는 구조

현재 책임 분리는 적절해 보입니다.

useFavoriteMovers가 담당하는 내용은 모두 찜 목록 데이터를 화면에서 사용할 수 있는 상태로 만드는 과정에 포함되어 있습니다.

  • 인증 복구 및 CUSTOMER 여부 확인
  • 현재 사용자의 Query Scope 계산
  • 쿼리 실행 가능 여부 결정
  • API 응답을 화면용 기사님 모델로 변환
  • 인증 대기와 초기 조회 상태를 isInitialLoading으로 통합

이 로직을 컴포넌트에서 각각 처리하는 것보다 Hook에서 캡슐화하는 편이 FavoriteMoversSidebarFavoriteMoversContent의 책임을 줄이고, 같은 조회 정책을 재사용하기에도 좋아 보입니다.

현재처럼 조회와 직접 관련된 상태는 Hook에서 관리하고, 선택·삭제·모달 같은 화면 상호작용은 별도 Hook으로 유지하면 책임도 과도하게 커지지 않을 것 같습니다.

2. 비회원·CUSTOMER·MOVER별 찜 버튼 노출과 Mutation 조건

현재 정책은 전반적으로 자연스럽습니다.

  • 비회원: 하트 버튼을 누르면 로그인 안내
  • CUSTOMER: 찜 추가·해제 가능
  • MOVER: 공개 기사 페이지는 조회할 수 있지만 하트는 읽기 전용
  • ADMIN: 공개 기사 페이지를 허용한다면 하트는 읽기 전용

/movers는 공개 페이지이므로 MOVER의 페이지 접근 자체를 막기보다, CUSTOMER 전용 기능인 찜 Mutation만 차단하는 현재 방향이 적절해 보입니다.

다만 사이드바도 같은 정책으로 맞춰야 합니다. 현재는 MOVER만 숨기고 있어 ADMIN에게 비회원 안내가 표시될 수 있으므로, 인증된 비고객 사용자를 공통으로 처리하도록 보완하면 버튼·사이드바 정책이 일관될 것 같습니다.

3. RoleGuard와 Hook의 CUSTOMER 검증이 중복인지

Hook의 CUSTOMER 검증은 유지하는 것이 좋다고 생각합니다.

RoleGuard와 Hook의 역할은 서로 다릅니다.

  • RoleGuard: 페이지 접근과 화면 전환을 제어하는 UX 계층
  • Mutation Hook: 실제 데이터 변경 요청을 실행할 수 있는지 보장하는 최종 방어선

특히 useFavoriteMover는 CUSTOMER 보호 페이지뿐 아니라 공개 기사 목록·상세, 견적 카드·상세 등 다양한 위치에서 재사용되고 있습니다. Hook의 검증을 제거하면 각 호출부가 권한 검사를 빠뜨리지 않도록 모두 관리해야 하므로 누락 위험이 커질 수 있습니다.

프론트 Hook의 검증이 백엔드 인가를 대신할 수는 없지만, 불필요한 요청을 방지하고 호출부의 정책을 일관되게 만드는 안전장치로 유지하는 방향이 적절해 보입니다.

4. 사용자별 Query Key와 Scope 기반 Invalidation으로 캐시 혼합을 충분히 방지하는지

현재 구조라면 충분히 방지할 수 있어 보입니다.

로그인 사용자의 캐시를 단순한 "authenticated"가 아니라 user:${userId}로 구분하고 있으며, 기사 목록·기사 상세·찜 목록이 동일한 사용자 Scope 정책을 사용하고 있습니다.

Mutation에서도 현재 Scope에 속한 Query만 대상으로 다음 작업을 수행하고 있습니다.

  • Query 취소
  • 기존 캐시 스냅샷 저장
  • 낙관적 업데이트
  • 오류 발생 시 복구
  • 완료 후 Invalidation

따라서 사용자 A의 찜 변경이 사용자 B나 Guest 캐시를 수정하거나 무효화하지 않는 구조입니다. 인증은 되었지만 userId가 아직 확정되지 않은 상태에서는 Query 실행을 막고 있어 인증 복구 구간도 함께 고려된 것으로 보입니다.

5. RoleGuardloadingFallback 방식

현재 방식이 적절해 보입니다.

찜 목록 페이지가 CUSTOMER Protected Route로 이동하면서 페이지 컴포넌트가 렌더링되기 전에 RoleGuard가 인증 상태를 확인하게 됩니다. 이때 별도의 Fallback이 없다면 인증 확인 동안 빈 화면이 노출될 수 있습니다.

기존 찜 목록의 헤더·컨테이너·스켈레톤을 loadingFallback으로 재사용한 방식은 다음 장점이 있습니다.

  • 인증 확인 중에도 기존 페이지 레이아웃 유지
  • 인증 완료 후 실제 목록으로 자연스럽게 전환
  • 페이지 내부의 중복 CustomerAuthGate 제거
  • 접근 제어와 인증 대기 UI를 보호 레이아웃에서 일관되게 관리

다른 Protected Page에도 서로 다른 로딩 UI가 필요해진다면 현재처럼 RoleGuard가 Fallback을 외부에서 주입받는 구조가 재사용하기에도 적절해 보입니다.

Comment thread src/hooks/useFavoriteMovers.ts

@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: 1

🤖 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/hooks/useFavoriteMovers.ts`:
- Around line 31-34: Update the isInitialLoading condition in useFavoriteMovers
to include isAuthQueryReady, so authentication is considered loading until the
auth query is ready even when no user exists after a network error. Keep the
existing loading conditions unchanged otherwise.
🪄 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: 311ec354-0cee-43a8-9ca7-c36b79c01f22

📥 Commits

Reviewing files that changed from the base of the PR and between 891e9ee and 1dce2ee.

📒 Files selected for processing (2)
  • src/components/mover/favorites/FavoriteMoversSidebar.tsx
  • src/hooks/useFavoriteMovers.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/mover/favorites/FavoriteMoversSidebar.tsx

Comment thread src/hooks/useFavoriteMovers.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants