Skip to content

refactor: 기사님 상세 구조 개선 및 사용자 역할별 액션 분리 - #53

Merged
yooseohyeon merged 9 commits into
devfrom
refactor/mover-detail-page
Aug 3, 2026
Merged

refactor: 기사님 상세 구조 개선 및 사용자 역할별 액션 분리#53
yooseohyeon merged 9 commits into
devfrom
refactor/mover-detail-page

Conversation

@yooseohyeon

@yooseohyeon yooseohyeon commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

📋 작업 내용

기사님 상세 페이지에 집중되어 있던 조회 상태, 지정 견적 요청, 역할별 액션, 리뷰 및 공유 로직을 각각의 hook과 컴포넌트로 분리했습니다.
견적 상세와 기사님 상세에서 함께 사용하는 히어로 배너도 공통 컴포넌트로 이동했습니다.


🔥 변경 사항

상세 조회 상태 캡슐화

  • 인증 상태 확인, 초기 로딩 및 404 판별을 useMoverDetail 내부로 이동했습니다.
  • MoverDetailView에서 Zustand 인증 상태와 API 오류 타입에 대한 직접적인 의존성을 제거했습니다.
  • 인증 scope가 확정된 이후에만 사용자별 기사님 상세 정보를 조회하도록 기존 동작을 유지했습니다.

지정 견적 요청 로직 분리

  • 활성 견적 요청 조회와 CTA 상태 계산을 useMoverDesignation으로 분리했습니다.
  • 지정 요청 버튼 상태·문구, 실패 후 재조회, 지정 요청 mutation 및 모달 상태를 훅에서 관리하도록 변경했습니다.
  • MoverDetailView가 상세 화면 조합과 상태별 UI 렌더링에 집중하도록 정리했습니다.

사용자 역할별 상세 액션 분리

  • CUSTOMER로 확인된 사용자만 활성 견적 요청 API를 호출하도록 수정했습니다.
  • 비회원은 기존과 동일하게 고객용 액션 클릭 시 로그인 안내 모달을 표시합니다.
  • MOVER에게는 찜 및 지정 견적 요청 등 고객 전용 액션을 노출하지 않도록 처리했습니다.
  • MOVER에게 찜 개수는 읽기 전용으로 표시하고 공유 기능은 유지했습니다.
  • 인증됐지만 역할을 확인할 수 없는 상태에서는 고객 전용 API와 액션을 차단했습니다.

리뷰 UI 컴포넌트 분리

  • 평균 별점과 별점 분포 UI를 MoverRatingSummary로 분리했습니다.
  • 리뷰 항목 목록을 MoverReviewList로 분리했습니다.
  • MoverDetailReviews에는 리뷰 조회, 로딩·오류·빈 상태 및 페이지네이션 조합만 남겼습니다.
  • 기존 리뷰 조회 및 페이지 전환 동작은 유지했습니다.

공유 책임 정리

  • 카카오 공유 템플릿 데이터 생성을 MoverDetailShare 내부로 이동했습니다.
  • MoverDetailView는 기사 이름, 찜 수, 프로필 이미지 등 화면 데이터만 전달하도록 변경했습니다.

상세 히어로 배너 공통화

  • EstimateDetailHero를 공통 상세 히어로 배너 컴포넌트로 변경하고 공통 컴포넌트 영역으로 이동했습니다.
  • 견적 상세와 기사님 상세가 동일한 히어로 배너를 사용하도록 import 및 사용처를 정리했습니다.
  • 프로필 표시 및 배너 전용 모드는 기존 동작을 유지했습니다.

접근성 개선 (리뷰 반영)

  • 읽기 전용 찜 요약에 role="group"을 추가해 aria-label의 현재 찜 개수가 스크린리더에 전달되도록 수정 (components/mover/detail/MoverDetailProfile.tsx)

✅ 체크리스트

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

📷 스크린샷 (선택)

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


💬 To Reviewer

  • MOVER의 기사님 찾기/기사님 상세 페이지 접근을 차단할지 말지 아직 정책이 정해지지 않은 관계로, 우선 MOVER의 접근을 허용하되 찜 등록/해제, 지정 견적 요청, 활성 견적 요청 조회가 불가능하도록 했습니다. 추후 정책이 정해지면 비회원과 CUSTOMER은 허용하되, MOVER는 차단하는 가드를 적용하거나 새롭게 만들 예정입니다! 어떻게 처리하는 게 좋을지 의견 부탁드립니다.
  • 비회원, CUSTOMER, MOVER 상태에서 기사님 상세의 찜 및 지정 견적 요청 액션이 의도한 대로 구분되는지 확인 부탁드립니다.
  • 상세 조회의 로딩·오류·404 상태가 기존과 동일하게 동작하는지 확인 부탁드립니다.
  • 리뷰 UI 분리 후 별점 분포, 리뷰 목록 및 페이지네이션이 정상적으로 표시되는지 확인 부탁드립니다.
  • 공통 히어로 배너의 위치와 명칭이 견적 상세 및 기사님 상세에서 함께 사용하기에 적절한지 의견 부탁드립니다.

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: 19c97348-73fb-425a-9be0-d3bcfc3611ec

📥 Commits

Reviewing files that changed from the base of the PR and between d79f9fc and 0cc8611.

📒 Files selected for processing (2)
  • src/components/common/DetailHeroBanner.tsx
  • src/hooks/useMoverDesignation.ts

📝 Walkthrough

Walkthrough

이 PR은 이사 상세 페이지의 리뷰 UI를 분리하고, 지정 흐름을 useMoverDesignation 훅으로 통합합니다. 공통 히어로 배너와 공유 props를 변경합니다. 상세 조회는 초기 로딩, 미존재 오류와 원본 query를 반환합니다.

Changes

이사 상세 페이지 변경

Layer / File(s) Summary
공통 히어로 배너 계약
src/components/common/DetailHeroBanner.tsx, src/components/estimate/detail/EstimateDetailLayout.tsx
EstimateDetailHero의 인터페이스와 컴포넌트 이름을 DetailHeroBanner로 변경했습니다. estimate 상세 레이아웃이 새 이름을 사용합니다.
리뷰 요약 및 목록 렌더링
src/components/mover/detail/MoverRatingSummary.tsx, src/components/mover/detail/MoverReviewList.tsx, src/components/mover/detail/MoverDetailReviews.tsx
평균 평점과 분포 UI를 MoverRatingSummary로 분리했습니다. 후기 목록과 로딩 상태 렌더링을 MoverReviewList로 분리했습니다.
지정 상태 및 상세 조회 관리
src/hooks/useMoverDesignation.ts, src/hooks/useMoverDetail.ts
인증, 활성 견적 요청, 지정 mutation과 모달 상태를 useMoverDesignation에서 관리합니다. useMoverDetail은 초기 로딩, 미존재 오류와 원본 query를 반환합니다.
상세 화면 액션 및 공유 구성
src/components/mover/detail/MoverDetailView.tsx, src/components/mover/detail/MoverDetailProfile.tsx, src/components/mover/detail/MoverDetailShare.tsx
지정 상태에 따라 고객 액션과 모달을 표시합니다. showFavoriteAction으로 찜 액션을 제어합니다. 공유 템플릿 인자를 상세 프로필 정보로 내부 생성합니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant Customer
  participant MoverDetailView
  participant useMoverDesignation
  participant DesignationMutation
  Customer->>MoverDetailView: 지정 CTA 클릭
  MoverDetailView->>useMoverDesignation: 요청 핸들러 실행
  useMoverDesignation->>DesignationMutation: 지정 mutation 호출
  DesignationMutation-->>useMoverDesignation: 성공 결과 반환
  useMoverDesignation-->>MoverDetailView: 성공 모달 상태 반환
  MoverDetailView-->>Customer: 성공 모달 표시
Loading

Possibly related PRs

Suggested labels: 🏢코드리뷰

Suggested reviewers: 9g-g9

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 기사님 상세 구조 개선과 사용자 역할별 액션 분리를 명확하게 요약하며 변경 사항의 주요 목적과 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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-detail-page

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 (2)
src/components/mover/detail/MoverDetailView.tsx (1)

87-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

고정 레이아웃 값을 디자인 토큰으로 대체하세요.

pb-[110px], lg:pb-[150px], lg:w-[766px]는 컴포넌트에 직접 지정된 값입니다. 기존 spacing 또는 layout 토큰을 사용하세요. 적절한 토큰이 없으면 의미 있는 토큰을 정의한 뒤 사용하세요.

As per path instructions, 색상·간격·radius는 src/styles/tokens.theme.css의 디자인 토큰을 사용해야 합니다.

🤖 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/detail/MoverDetailView.tsx` around lines 87 - 92,
MoverDetailView의 레이아웃에서 직접 지정한 pb-[110px], lg:pb-[150px], lg:w-[766px] 값을 제거하고
src/styles/tokens.theme.css의 기존 spacing/layout 디자인 토큰으로 교체하세요. 적절한 토큰이 없으면 의미가
드러나는 토큰을 tokens.theme.css에 정의한 뒤 해당 토큰을 사용하고, DetailHeroBanner 및 주변 flex 레이아웃
동작은 유지하세요.

Sources: Coding guidelines, Path instructions

src/components/mover/detail/MoverDetailProfile.tsx (1)

121-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

FavoriteSummary의 props 타입에 이름을 붙여 주세요.

{ detail: MoverDetail } 인라인 타입 대신 FavoriteSummaryProps 같은 명명된 interface를 정의해 주세요. 다른 컴포넌트(MoverDetailProfileProps)와 동일한 규칙을 따르게 됩니다.

♻️ 제안하는 리팩터
-function FavoriteSummary({ detail }: { detail: MoverDetail }) {
+interface FavoriteSummaryProps {
+  detail: MoverDetail;
+}
+
+function FavoriteSummary({ detail }: FavoriteSummaryProps) {

As per path instructions, "Props 는 interface XxxProps 형태로 명확히 정의합니다."

🤖 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/detail/MoverDetailProfile.tsx` around lines 121 - 141,
FavoriteSummary의 인라인 props 타입을 제거하고, detail: MoverDetail을 포함하는 명명된
FavoriteSummaryProps interface를 정의한 뒤 컴포넌트 props 타입으로 사용하세요. 기존 렌더링 동작과
MoverDetailProfileProps의 명명 규칙은 유지하세요.

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/detail/MoverDetailProfile.tsx`:
- Around line 60-67: showFavoriteAction이 false인 분기에서 찜 요약을 감싸는 div에
role="group"을 추가하세요. 기존 aria-label인 `현재 찜 ${detail.favoriteCount}개`는 유지해 보조공학이
비활성 찜 요약을 이름 있는 그룹으로 인식하도록 하고, FavoriteSummary 렌더링은 변경하지 마세요.

---

Nitpick comments:
In `@src/components/mover/detail/MoverDetailProfile.tsx`:
- Around line 121-141: FavoriteSummary의 인라인 props 타입을 제거하고, detail: MoverDetail을
포함하는 명명된 FavoriteSummaryProps interface를 정의한 뒤 컴포넌트 props 타입으로 사용하세요. 기존 렌더링 동작과
MoverDetailProfileProps의 명명 규칙은 유지하세요.

In `@src/components/mover/detail/MoverDetailView.tsx`:
- Around line 87-92: MoverDetailView의 레이아웃에서 직접 지정한 pb-[110px], lg:pb-[150px],
lg:w-[766px] 값을 제거하고 src/styles/tokens.theme.css의 기존 spacing/layout 디자인 토큰으로
교체하세요. 적절한 토큰이 없으면 의미가 드러나는 토큰을 tokens.theme.css에 정의한 뒤 해당 토큰을 사용하고,
DetailHeroBanner 및 주변 flex 레이아웃 동작은 유지하세요.
🪄 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: 23da9d20-8619-457e-8537-6af2d1bffe64

📥 Commits

Reviewing files that changed from the base of the PR and between e4d18e1 and 9429a3a.

📒 Files selected for processing (10)
  • src/components/common/DetailHeroBanner.tsx
  • src/components/estimate/detail/EstimateDetailLayout.tsx
  • src/components/mover/detail/MoverDetailProfile.tsx
  • src/components/mover/detail/MoverDetailReviews.tsx
  • src/components/mover/detail/MoverDetailShare.tsx
  • src/components/mover/detail/MoverDetailView.tsx
  • src/components/mover/detail/MoverRatingSummary.tsx
  • src/components/mover/detail/MoverReviewList.tsx
  • src/hooks/useMoverDesignation.ts
  • src/hooks/useMoverDetail.ts

Comment thread src/components/mover/detail/MoverDetailProfile.tsx
@yooseohyeon
yooseohyeon requested review from 9g-g9 and removed request for soooob43 August 1, 2026 08:18

@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 리뷰

👍 좋았던 점

  • 기사님 상세 조회의 인증 복구, 사용자별 Query Scope 확인, 초기 로딩과 404 판별을 useMoverDetail로 이동해 화면 컴포넌트의 상태 의존성을 줄였습니다.
  • 사용자 Scope가 확정되기 전에는 상세 Query를 실행하지 않도록 기존 optional API 캐시 정책을 유지했습니다.
  • 활성 견적 요청 조회, CTA 계산, 실패 후 재조회, 지정 요청 Mutation과 모달 상태를 useMoverDesignation으로 분리해 지정 견적 요청의 흐름을 한곳에서 관리하도록 정리했습니다.
  • 활성 견적 요청 API를 인증된 CUSTOMER에게만 호출하도록 제한해 비회원·MOVER·역할 미확정 사용자의 불필요한 고객 전용 API 호출을 방지했습니다.
    비회원에게는 기존 로그인 안내 흐름을 유지하고, MOVER에게는 고객 전용 액션을 숨기면서 공유와 공개 정보 조회는 유지했습니다.
  • MOVER에게 찜 개수는 읽기 전용으로 제공하고 접근성 레이블까지 추가해 인터랙션 여부를 명확하게 구분했습니다.
  • 평균 별점과 분포를 MoverRatingSummary, 리뷰 목록을 MoverReviewList로 분리해 MoverDetailReviews가 조회 상태와 페이지네이션 조합에 집중하도록 개선했습니다.
  • 카카오 공유 데이터 가공을 MoverDetailShare 내부로 이동해 MoverDetailView가 화면 데이터를 전달하는 역할만 담당하도록 정리했습니다.
  • 견적 상세와 기사님 상세에서 함께 사용하는 히어로 배너를 DetailHeroBanner로 공통화해 실제 사용 범위와 이름을 일치시켰습니다.
  • MoverDetailView에서 인증 Store, API 오류 타입, 활성 요청 Query, CTA 계산과 모달 상태가 제거되어 상세 화면의 조합 구조가 훨씬 읽기 쉬워졌습니다.

🔍 확인 및 제안

한 가지 가벼운 제안으로는 DetailHeroBanner.tsx의 주석이 아직 “견적 상세 히어로 배너”로 남아 있어, 공통화된 현재 역할에 맞게 “상세 페이지 공통 히어로 배너” 정도로 함께 수정하면 이름과 설명이 더 일관될 것 같습니다. 기능상 문제는 아닙니다.

전체적으로 기존 동작을 유지하면서 기사님 상세의 조회, 역할, 지정 견적, 리뷰, 공유 책임을 깔끔하게 분리한 리팩토링이었습니다. 수고하셨습니다! 😊


To Reviewer 질문에 대한 답변드립니다!

현재 MOVER의 기사님 찾기와 기사님 상세 접근 정책이 확정되지 않은 상황에서는,
공개 페이지 접근은 허용하되 CUSTOMER 전용 데이터 변경 액션만 차단하는 현재 방식이
가장 안전한 기본값으로 보입니다.

기사님 목록과 상세에는 공개 프로필, 서비스 정보, 리뷰 등 MOVER가 확인해도 문제가 없는
정보가 포함되어 있고 공유 기능도 공개 기능에 해당하므로, 정책 확정 전부터 페이지 전체를
차단할 필요는 크지 않아 보입니다. 추후 서비스 정책상 MOVER의 기사 검색 자체를 막기로
결정된다면 그때 Route Guard를 추가하고, Hook의 CUSTOMER 검증은 최종 방어선으로
그대로 유지하는 방향이 좋을 것 같습니다.

역할별 액션도 코드상 다음과 같이 구분되어 있는 것을 확인했습니다.

  • 비회원: 기사 상세 조회 및 공유 가능, 찜·지정 요청 클릭 시 로그인 안내
  • CUSTOMER: 찜 등록·해제, 지정 견적 요청, 활성 견적 요청 조회 가능
  • MOVER: 기사 상세 조회 및 공유 가능, 찜 개수만 읽기 전용으로 표시하며 고객 전용 액션은 숨김
  • 역할 미확정 사용자: 고객 전용 API와 액션 모두 차단

useActiveEstimateRequest()isCustomerLoggedIn인 경우에만 활성화되어 있어
MOVER나 역할 미확정 사용자에게 고객 전용 API가 호출되지 않는 흐름이 적절해 보였습니다.

상세 조회의 상태 처리도 기존 흐름을 유지하고 있습니다.
인증 hydrate와 세션 확인, 사용자별 Scope 확정 및 Query 초기 로딩을
isInitialLoading으로 묶어 기존처럼 스켈레톤을 표시하고,
MOVER_NOT_FOUND 또는 404는 별도 Not Found 상태로,
그 외 오류는 재시도 가능한 Error Panel로 분기하고 있었습니다.

리뷰 UI는 평균 별점·별점 분포를 MoverRatingSummary,
리뷰 항목을 MoverReviewList로 옮겼지만 조회 데이터와
페이지네이션 조건은 MoverDetailReviews에 그대로 유지되어 있어
기존 별점 분포, 리뷰 목록, 페이지 전환 동작에도 영향이 없어 보였습니다.

공통 히어로 배너의 위치와 이름도 현재 구조가 적절해 보입니다.
견적 상세와 기사님 상세에서 함께 사용하며 showProfile을 통해
프로필 포함·배너 전용 모드를 모두 지원하므로,
components/common/DetailHeroBanner가 기존 EstimateDetailHero보다
실제 책임을 더 정확하게 표현한다고 생각합니다.

다만 컴포넌트 내부 주석에 남아 있는 “견적 상세 히어로 배너” 표현도
공통 역할에 맞게 함께 수정하면 이름과 문서가 더 일관될 것 같습니다.

@yooseohyeon
yooseohyeon force-pushed the refactor/mover-detail-page branch from 2027647 to d79f9fc Compare August 3, 2026 05:06
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