feat: 기사님 상세·리뷰 조회 API 연동 및 로딩/에러 UI 추가 - #29
Conversation
백엔드 시드에 Review row가 없어 페이지네이션 확인용 mock을 함께 포함합니다. Co-authored-by: Cursor <cursoragent@cursor.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthrough무버 상세 페이지를 mock 데이터에서 API·React Query 기반으로 전환했습니다. 상세·리뷰 조회, 페이지네이션, 로딩·오류·404 상태, 카드 상세 링크와 즐겨찾기 캐시 동기화가 추가되었습니다. Changes무버 상세 데이터 흐름
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant MoverDetailView
participant useMoverDetail
participant getMoverDetail
participant MoverDetailReviews
participant useMoverReviews
Browser->>MoverDetailView: moverId로 상세 페이지 요청
MoverDetailView->>useMoverDetail: 상세 조회
useMoverDetail->>getMoverDetail: 상세 API 호출
getMoverDetail-->>useMoverDetail: 상세 응답
useMoverDetail-->>MoverDetailView: 변환된 상세 데이터
MoverDetailView->>MoverDetailReviews: moverId와 별점 정보 전달
MoverDetailReviews->>useMoverReviews: page, limit 조회
useMoverReviews-->>MoverDetailReviews: 리뷰 및 pagination 반환
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)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/components/mover/MoverCard.tsx (1)
104-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win공통 그림자 토큰을 사용하세요.
동일한 RGBA 기반 arbitrary shadow가 카드 변형마다 중복되어 있습니다. 기존 semantic shadow 토큰으로 교체해 디자인 변경 시 한 곳에서 관리되게 하세요.
src/components/mover/MoverCard.tsx#L104-L104: arbitrary shadow를 프로젝트 shadow 토큰 유틸리티로 교체하세요.src/components/mover/MoverCard.tsx#L159-L159: 동일한 토큰 유틸리티를 재사용하세요.As per coding guidelines, “Use the project's existing design tokens for … shadows instead of hardcoding design values.”
🤖 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/MoverCard.tsx` at line 104, Replace the duplicated arbitrary RGBA shadow at src/components/mover/MoverCard.tsx lines 104-104 and 159-159 with the same existing semantic shadow-token utility, reusing one project-defined token for both MoverCard variants.Sources: Coding guidelines, Path instructions
src/hooks/useFavoriteMover.ts (1)
83-83: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win상세 쿼리 키 접두사를 상수로 분리해 사용하세요.
"detail"을 직접 조합하면QUERY_KEYS.MOVERS.DETAIL()구조 변경 시 이 무효화만 누락될 수 있습니다.DETAIL_ROOT를 정의하고 상세 키 생성과 여기서 함께 재사용하세요.As per path instructions, “TanStack Query의 쿼리 키는
queryKeys.ts의QUERY_KEYS를 사용합니다.”🤖 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/useFavoriteMover.ts` at line 83, Define a shared `DETAIL_ROOT` constant in the `QUERY_KEYS.MOVERS` configuration, use it when constructing `QUERY_KEYS.MOVERS.DETAIL()`, and replace the literal `"detail"` in the `invalidateQueries` call within `useFavoriteMover` with that shared constant while continuing to use `QUERY_KEYS` for the query key.Sources: Coding guidelines, 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/detail/MoverDetailReviews.tsx`:
- Around line 132-169: Update the reviews rendering in MoverDetailReviews so
MoverDetailReviewsSkeleton is shown only during initial isLoading, while page
transitions with isFetching keep the existing reviews visible and expose loading
through aria-busy on the list or its container. Keep previously loaded reviews
rendered when isError occurs, showing the inline error alongside them instead of
hiding the list.
In `@src/components/mover/detail/MoverDetailView.tsx`:
- Around line 37-55: Update the error/empty-state condition in MoverDetailView
so the full-screen not-found or error panels render only when detail is absent,
preserving already loaded content when isError is true alongside detail. Keep
the existing isMoverNotFoundError handling for missing data, and handle
background refetch errors through the component’s established non-blocking error
UI if available.
In `@src/components/mover/MoversErrorPanel.tsx`:
- Around line 22-31: Update the dynamic error message wrapper in
MoversErrorPanel’s returned JSX to include role="alert", ensuring the rendered
title and description are announced by screen readers while preserving the
existing layout and text elements.
---
Nitpick comments:
In `@src/components/mover/MoverCard.tsx`:
- Line 104: Replace the duplicated arbitrary RGBA shadow at
src/components/mover/MoverCard.tsx lines 104-104 and 159-159 with the same
existing semantic shadow-token utility, reusing one project-defined token for
both MoverCard variants.
In `@src/hooks/useFavoriteMover.ts`:
- Line 83: Define a shared `DETAIL_ROOT` constant in the `QUERY_KEYS.MOVERS`
configuration, use it when constructing `QUERY_KEYS.MOVERS.DETAIL()`, and
replace the literal `"detail"` in the `invalidateQueries` call within
`useFavoriteMover` with that shared constant while continuing to use
`QUERY_KEYS` for the query key.
🪄 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: 90ca0c95-b83d-43d8-8b5f-8dd5c08c1cf2
📒 Files selected for processing (24)
src/app/favorites/movers/page.tsxsrc/app/movers/[moverId]/error.tsxsrc/app/movers/[moverId]/not-found.tsxsrc/app/movers/[moverId]/page.tsxsrc/components/mover/MoverCard.tsxsrc/components/mover/MoversErrorPanel.tsxsrc/components/mover/MoversList.tsxsrc/components/mover/detail/MoverDetailActions.tsxsrc/components/mover/detail/MoverDetailNotFoundStatus.tsxsrc/components/mover/detail/MoverDetailPageSkeleton.tsxsrc/components/mover/detail/MoverDetailReviews.tsxsrc/components/mover/detail/MoverDetailView.tsxsrc/components/mover/detail/moverDetailMock.tssrc/hooks/useFavoriteMover.tssrc/hooks/useMoverDetail.tssrc/hooks/useMoverReviews.tssrc/lib/api/movers.tssrc/lib/constants/apiRoutes.tssrc/lib/constants/queryKeys.tssrc/lib/utils/isMoverDetailId.tssrc/lib/utils/mapMover.tssrc/types/mover.tssrc/types/moverDetail.tssrc/types/review.ts
💤 Files with no reviewable changes (1)
- src/components/mover/detail/moverDetailMock.ts
juengseulki
left a comment
There was a problem hiding this comment.
📋 PR 리뷰
👍 좋았던 점
- 기사님 상세 정보와 리뷰 목록을 별도의 API 및 TanStack Query Hook으로 분리해 각 데이터의 로딩과 오류 상태를 독립적으로 관리했습니다.
- 잘못된 UUID는 서버 단계에서 notFound()로 처리해 불필요한 API 요청을 방지했습니다.
- 유효한 UUID의 상세 조회에서 404가 반환된 경우와 일반 네트워크 오류를 구분해 각각 적절한 화면을 제공했습니다.
- 리뷰 API만 실패했을 때 상세 페이지 전체를 오류 화면으로 전환하지 않고 리뷰 영역에서만 오류와 재시도 UI를 제공한 점이 좋았습니다.
- 상세 조회와 리뷰 조회에 각각 스켈레톤을 적용해 초기 데이터 로딩 상태를 명확하게 표현했습니다.
- 리뷰 페이지 번호와 limit를 Query Key에 포함해 페이지별 데이터가 서로 다른 캐시로 관리되도록 구성했습니다.
- 백엔드의 별점 분포를 5점부터 1점까지 고정된 형태로 정규화하고, 누락된 점수도 0으로 보정해 UI에서 안정적으로 사용할 수 있도록 했습니다.
- 최다 리뷰 점수가 여러 개일 때 5점부터 1점 순서 중 하나만 강조하도록 기준을 명확히 한 점도 좋았습니다.
- 기사님 카드 전체를 상세 페이지 링크로 연결하면서 찜 버튼에서는 이벤트 전파를 차단해 카드 이동과 찜 동작이 충돌하지 않도록 처리했습니다.
🔍 확인 및 제안
인라인 코멘트로 아래 내용을 남겼습니다.
- 리뷰 Query에는 keepPreviousData가 적용되어 있지만, 페이지 변경 중 isFetching이면 기존 목록을 숨기고 전체 스켈레톤을 표시하고 있습니다. 페이지 전환마다 스켈레톤으로 교체하려는 의도인지 확인 부탁드립니다.
현재 시드 데이터의 reviewCount와 실제 리뷰 목록이 일치하지 않는 문제는 프론트 로직보다는 백엔드 데이터 정합성 문제로 보이며, 이번 PR에서 리뷰 배열의 실제 결과를 기준으로 Empty UI를 표시하도록 방어한 것은 테스트 단계의 임시 대응으로 이해했습니다.
전체적으로 상세 정보와 리뷰를 실제 API로 자연스럽게 전환했고, 전체 오류와 부분 오류를 구분해 화면 사용성을 잘 유지했습니다. 수고하셨습니다! 😊
📋 작업 내용
기사님 상세(
/movers/[moverId]) 페이지에서 기사님 상세 정보와 리뷰 목록을 mock에서 실제 API로 전환했습니다. 또한 로딩/404/에러 상태를 추가했습니다.🔥 변경 사항
GET /movers/:id상세 조회 연동 (useMoverDetail)GET /movers/:id/reviews리뷰 목록 연동 (useMoverReviews, 페이지네이션)ratingDistribution반영 (최다 점수만 bold)notFound()MoversErrorPanel[moverId]/not-found.tsx,error.tsx추가✅ 체크리스트
📷 스크린샷 (선택)
정상 (리뷰·별점 분포)
로딩 UI
EmptyState
에러 UI
💬 To Reviewer
reviewCount와 실제 리뷰 목록이 불일치합니다. 리뷰 많은순 정렬 적용 시 상단 기사님들의 리뷰 목록이 비어 empty가 보입니다. 리뷰·분포 확인은 빠른기사, 든든기사, 베테랑기사를 사용해 주세요. 백엔드 시드 수정이 필요합니다.준비 중토스트입니다. 이후 별도로 작업할 예정입니다.notFound()(없는 기사)는 이번 범위에서 제외했습니다. 현재는 UUID만 서버에서 거르고, 본문/리뷰는 클라이언트 조회+스켈레톤/EmptyState로 처리합니다. 상세는 단건 조회라 SSR prefetch을 적용했을 때 초기 체감 속도가 줄어드는 장점이 덜하기 때문입니다. 더불어 이번 PR 목표를 벗어나기 때문에도 제외했습니다.Summary by CodeRabbit