Skip to content

feat: 기사님 상세·리뷰 조회 API 연동 및 로딩/에러 UI 추가 - #29

Merged
yooseohyeon merged 12 commits into
devfrom
feature/mover-detail-api
Jul 29, 2026
Merged

feat: 기사님 상세·리뷰 조회 API 연동 및 로딩/에러 UI 추가#29
yooseohyeon merged 12 commits into
devfrom
feature/mover-detail-api

Conversation

@yooseohyeon

@yooseohyeon yooseohyeon commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

📋 작업 내용

기사님 상세(/movers/[moverId]) 페이지에서 기사님 상세 정보와 리뷰 목록을 mock에서 실제 API로 전환했습니다. 또한 로딩/404/에러 상태를 추가했습니다.


🔥 변경 사항

  • GET /movers/:id 상세 조회 연동 (useMoverDetail)
  • GET /movers/:id/reviews 리뷰 목록 연동 (useMoverReviews, 페이지네이션)
  • 상세 응답 ratingDistribution 반영 (최다 점수만 bold)
  • 상세·리뷰 로딩 스켈레톤 추가
  • 잘못된 UUID → notFound()
  • 네트워크 에러 → 기사님 찾기와 동일한 MoversErrorPanel
  • [moverId]/not-found.tsx, error.tsx 추가
  • 리뷰 섹션 에러만 실패할 경우 해당 부분에만 인라인 에러 UI 표시

✅ 체크리스트

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

📷 스크린샷 (선택)

정상 (리뷰·별점 분포)

localhost_3000_movers (14)

로딩 UI

localhost_3000_movers_45a6a99c-a979-4ac7-9590-7f57cf753651 (1)

EmptyState

localhost_3000_movers_45a6a99c-a979-4ac7-9590-7f57cf75

에러 UI

localhost_3000_movers_45a6a99c-a979-4ac7-9590-7f57cf753651 (2)

💬 To Reviewer

⚠️ 일부 기사는 reviewCount와 실제 리뷰 목록이 불일치합니다. 리뷰 많은순 정렬 적용 시 상단 기사님들의 리뷰 목록이 비어 empty가 보입니다. 리뷰·분포 확인은 빠른기사, 든든기사, 베테랑기사를 사용해 주세요. 백엔드 시드 수정이 필요합니다.

  • 지정 견적 요청은 아직 준비 중 토스트입니다. 이후 별도로 작업할 예정입니다.
  • SNS 공유(카카오·페이스북 등)는 견적 상세와 맞추기 위해, 이번 PR에서 제외하고 추후 작업할 예정입니다.
  • 상세 페이지에서 SSR prefetch·서버 notFound()(없는 기사)는 이번 범위에서 제외했습니다. 현재는 UUID만 서버에서 거르고, 본문/리뷰는 클라이언트 조회+스켈레톤/EmptyState로 처리합니다. 상세는 단건 조회라 SSR prefetch을 적용했을 때 초기 체감 속도가 줄어드는 장점이 덜하기 때문입니다. 더불어 이번 PR 목표를 벗어나기 때문에도 제외했습니다.

Summary by CodeRabbit

  • 새 기능
    • 기사님 상세/리뷰를 실제 데이터로 불러오고, 리뷰를 페이지 단위로 조회할 수 있습니다.
    • 로딩 스켈레톤, 오류 재시도 패널, “없음(부재)” 상태 UI를 상세 화면에 제공하였습니다.
  • 버그 수정
    • 잘못된 기사님 식별자는 404 화면을 표시합니다.
    • 즐겨찾기 변경 시 상세 정보가 즉시 동기화됩니다.
    • 즐겨찾기 화면의 “기사님 찾기로 돌아가기” 버튼 이동 경로를 수정했습니다.
    • 카드에서 즐겨찾기 클릭 시 상세 이동이 발생하지 않도록 개선했습니다.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b9bd315-cc62-4672-807e-74ba4d5f9d3b

📥 Commits

Reviewing files that changed from the base of the PR and between 03f5ad4 and 01e57fd.

📒 Files selected for processing (3)
  • src/components/mover/MoversErrorPanel.tsx
  • src/components/mover/detail/MoverDetailReviews.tsx
  • src/components/mover/detail/MoverDetailView.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/mover/MoversErrorPanel.tsx
  • src/components/mover/detail/MoverDetailReviews.tsx

📝 Walkthrough

Walkthrough

무버 상세 페이지를 mock 데이터에서 API·React Query 기반으로 전환했습니다. 상세·리뷰 조회, 페이지네이션, 로딩·오류·404 상태, 카드 상세 링크와 즐겨찾기 캐시 동기화가 추가되었습니다.

Changes

무버 상세 데이터 흐름

Layer / File(s) Summary
상세·리뷰 계약과 API 매핑
src/types/..., src/lib/api/movers.ts, src/lib/constants/..., src/lib/utils/...
상세·리뷰 응답 타입, API 경로, 쿼리 키, 요청 함수와 화면 모델 변환을 추가했습니다.
상세 조회와 페이지 상태
src/hooks/useMoverDetail.ts, src/components/mover/detail/..., src/app/movers/[moverId]/...
유효한 ID 검증과 상세 조회를 연결하고 로딩 스켈레톤, 404, 오류 재시도를 적용했습니다.
리뷰 조회와 페이지네이션
src/hooks/useMoverReviews.ts, src/components/mover/detail/MoverDetailReviews.tsx
리뷰를 페이지 단위로 조회하고 응답 변환, 로딩, 오류, 별점 분포, 페이지네이션을 처리합니다.
카드 탐색과 즐겨찾기 동기화
src/components/mover/MoverCard.tsx, src/components/mover/detail/..., src/hooks/useFavoriteMover.ts
카드 상세 링크와 즐겨찾기 클릭 격리를 추가하고 상세 캐시의 낙관적 갱신·롤백·무효화를 연결했습니다.
공통 오류 상태와 재시도
src/components/mover/MoversErrorPanel.tsx, src/components/mover/MoversList.tsx, src/app/favorites/movers/page.tsx
공용 오류 패널을 목록과 상세 오류 흐름에 적용하고 재시도 동작 및 무버 루트 링크를 정리했습니다.

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 반환
Loading

Possibly related PRs

Suggested labels: ♻️ refactor

Suggested reviewers: 9g-g9, youngmis

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 상세·리뷰 API 연동과 로딩/에러 UI 추가라는 핵심 변경을 간결하게 잘 요약합니다.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mover-detail-api

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: 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.tsQUERY_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

📥 Commits

Reviewing files that changed from the base of the PR and between 32e4eef and b021505.

📒 Files selected for processing (24)
  • src/app/favorites/movers/page.tsx
  • src/app/movers/[moverId]/error.tsx
  • src/app/movers/[moverId]/not-found.tsx
  • src/app/movers/[moverId]/page.tsx
  • src/components/mover/MoverCard.tsx
  • src/components/mover/MoversErrorPanel.tsx
  • src/components/mover/MoversList.tsx
  • src/components/mover/detail/MoverDetailActions.tsx
  • src/components/mover/detail/MoverDetailNotFoundStatus.tsx
  • src/components/mover/detail/MoverDetailPageSkeleton.tsx
  • src/components/mover/detail/MoverDetailReviews.tsx
  • src/components/mover/detail/MoverDetailView.tsx
  • src/components/mover/detail/moverDetailMock.ts
  • src/hooks/useFavoriteMover.ts
  • src/hooks/useMoverDetail.ts
  • src/hooks/useMoverReviews.ts
  • src/lib/api/movers.ts
  • src/lib/constants/apiRoutes.ts
  • src/lib/constants/queryKeys.ts
  • src/lib/utils/isMoverDetailId.ts
  • src/lib/utils/mapMover.ts
  • src/types/mover.ts
  • src/types/moverDetail.ts
  • src/types/review.ts
💤 Files with no reviewable changes (1)
  • src/components/mover/detail/moverDetailMock.ts

Comment thread src/components/mover/detail/MoverDetailReviews.tsx Outdated
Comment thread src/components/mover/detail/MoverDetailView.tsx Outdated
Comment thread src/components/mover/MoversErrorPanel.tsx

@juengseulki juengseulki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📋 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로 자연스럽게 전환했고, 전체 오류와 부분 오류를 구분해 화면 사용성을 잘 유지했습니다. 수고하셨습니다! 😊

Comment thread src/components/mover/detail/MoverDetailReviews.tsx Outdated
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