Skip to content

perf: 기사님 상세 초기 렌더링 및 CTA 로딩 개선 - #95

Merged
yooseohyeon merged 4 commits into
devfrom
perf/optimize-movers-detail-lcp
Aug 10, 2026
Merged

perf: 기사님 상세 초기 렌더링 및 CTA 로딩 개선#95
yooseohyeon merged 4 commits into
devfrom
perf/optimize-movers-detail-lcp

Conversation

@yooseohyeon

@yooseohyeon yooseohyeon commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

📋 작업 내용

  • 기사님 상세의 초기 렌더링과 LCP를 개선하고, 인증·사용자별 상태 확인 중 CTA가 뒤늦게 노출되며 발생하는 레이아웃 변화를 줄였습니다.

🔥 변경 사항

  • src/app/movers/[moverId]/page.tsx

    • 서버에서 prefetch한 기사님 상세 데이터를 클라이언트 화면으로 전달했습니다.
    • 인증 초기화 중에도 서버에서 받은 상세 정보를 우선 렌더링하도록 변경했습니다.
  • DetailHeroBanner

    • 기사님 상세 히어로 프로필 이미지를 LCP 대상으로 지정하고 preload를 적용했습니다.
  • MoverDetailView, MoverDetailActions, useMoverDesignation

    • 인증, 사용자별 찜 상태, 활성 견적 요청 상태를 확인하는 동안 CTA 영역에 스켈레톤을 표시합니다.
    • 상태가 확정된 뒤 실제 찜·지정 견적 버튼을 노출하도록 변경했습니다.
    • 데스크톱 CTA의 기사님 이름 안내 문구는 초기 상세 데이터로 즉시 표시합니다.
  • MoverDetailReviews

    • 현재 리뷰 페이지가 로드된 뒤 300ms 후 다음 페이지를 자동 prefetch하도록 추가했습니다.
    • 기존 hover·focus prefetch도 유지해, 페이지를 바로 클릭하는 경우에도 캐시 또는 진행 중인 요청을 재사용할 수 있도록 했습니다.

✅ 체크리스트

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

📷 스크린샷 (선택)

LCP 측정 비교 (5.97s → 1.45s)

동일 조건: 데스크톱 뷰포트, Slow 4G, Disable cache, 로컬에서 build && start

수정 전

스크린샷 2026-08-10 오전 9 37 11

수정 후

스크린샷 2026-08-10 오전 9 38 27

💬 To Reviewer

  • 서버 prefetch 상세 데이터를 초기 화면에 사용하고, 이후 기존 TanStack Query의 사용자별 상세 데이터로 전환하도록 구성했습니다.
  • 히어로 프로필 이미지만 preload 대상으로 제한해 초기 이미지 요청을 앞당겼습니다.
  • CTA는 인증 확인만이 아니라 사용자별 상세 찜 상태와 활성 견적 요청 상태까지 확정된 뒤 실제 버튼을 표시합니다. 따라서 스켈레톤 이후 비활성 버튼이 잠시 노출됐다가 활성화되는 전환을 방지했습니다.

멘토님께 드릴 질문

  1. 리뷰 목록은 현재 페이지가 표시된 뒤 300ms 후 다음 페이지를 자동 prefetch하고, 기존 hover·focus prefetch도 유지하고 있습니다. . 페이지 진입 직후 자동 prefetch까지 수행하는 것이 적절한지, 혹은 hover·focus처럼 사용자 상호작용이 있을 때만 prefetch하는 편이 나은지 궁금합니다. 또한 다음 페이지 한 개만 prefetch하는 현재 범위가 적절한지도 궁금합니다.
  2. 이번 PR에서는 로컬 production build 환경에서 Chrome DevTools의 Slow 4G·Disable cache 조건으로 LCP를 측정했습니다. 이외에 성능 개선 효과를 검증할 수 있는 다른 측정 도구나 지표에 무엇이 있을지, 그리고 이들을 도입할 때 어떤 기준을 가져야 하는지도 조언해주시면 감사하겠습니다.

Summary by CodeRabbit

  • 개선 사항
    • 이사 업체 상세 페이지가 초기 데이터를 더 빠르게 표시하도록 개선되었습니다.
    • 상세 정보와 프로필 이미지 로딩이 원활해져 첫 화면 표시 경험이 향상되었습니다.
    • 인증 및 견적 확인 중에도 실제 버튼 위치를 유지하는 로딩 UI가 표시됩니다.
    • 찜하기와 주요 액션이 이용 가능한 상태에 맞게 안정적으로 표시됩니다.
    • 다음 리뷰를 미리 불러와 리뷰 탐색이 더욱 원활해졌습니다.
  • 접근성
    • 액션 로딩 상태가 보조 기술에서도 인식되도록 개선되었습니다.

- 서버 prefetch 상세 데이터를 초기 화면에 사용
- 히어로 프로필 이미지 preload 적용
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
moving-frontend Ready Ready Preview Aug 10, 2026 6:31am
moving-frontend-p2ol Ready Ready Preview Aug 10, 2026 6:31am

@coderabbitai

coderabbitai Bot commented Aug 10, 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

Run ID: e59b464f-359c-4619-a986-a38507ec3213

📥 Commits

Reviewing files that changed from the base of the PR and between 950561c and b731a6b.

📒 Files selected for processing (1)
  • src/hooks/useMoverDesignation.ts
💤 Files with no reviewable changes (1)
  • src/hooks/useMoverDesignation.ts

📝 Walkthrough

Walkthrough

무버 상세 페이지가 서버 프리패치 데이터를 초기 화면에 사용합니다. 인증 또는 활성 견적 조회 중에는 액션 스켈레톤을 표시합니다. 프로필 이미지 preload와 리뷰 페이지 prefetch를 추가했습니다.

Changes

무버 상세 페이지 흐름

Layer / File(s) Summary
서버 프리패치 상세 정보 연결
src/app/movers/[moverId]/page.tsx, src/components/mover/detail/MoverDetailView.tsx
서버 프리패치 결과를 initialDetail로 전달합니다. displayedDetail이 있으면 이를 사용하고, 없을 때만 상세 로딩 스켈레톤을 표시합니다.
액션 로딩 상태와 스켈레톤 구성
src/hooks/useMoverDesignation.ts, src/components/mover/detail/MoverDetailActions.tsx, src/components/mover/detail/MoverDetailView.tsx
인증 대기와 활성 견적 조회 상태를 isActionsLoading으로 반환합니다. 로딩 중에는 sticky와 sidebar 영역에 접근성 속성을 포함한 액션 스켈레톤을 표시합니다.
상세 액션과 프로필 이미지 preload
src/components/common/DetailHeroBanner.tsx, src/components/mover/detail/MoverDetailView.tsx
액션 가능 상태에서 찜 액션과 CTA를 표시합니다. preloadProfileImage 값으로 프로필 이미지 preload를 제어합니다.
리뷰 페이지 prefetch
src/components/mover/detail/MoverDetailReviews.tsx
다음 리뷰 페이지가 있으면 300ms 후 prefetch합니다. 의존성 변경 또는 언마운트 시 타이머를 정리합니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 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 제목은 서버 프리패치 기반 초기 렌더링과 CTA 로딩 상태 개선이라는 PR의 주요 변경 사항을 정확히 요약합니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 perf/optimize-movers-detail-lcp

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

🤖 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/MoverDetailView.tsx`:
- Around line 118-121: Update the MoverDetailView rendering around
MoverDetailProfile so the favorite UI is hidden or replaced with a skeleton
while isInitialLoading is true; do not pass initialDetail’s guest favorite state
to the rendered FavoriteButton during authentication lookup, while preserving
the existing customer-action visibility after loading completes.
🪄 Autofix

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

Run ID: be408127-fea3-476f-bebb-8d93392fd2cf

📥 Commits

Reviewing files that changed from the base of the PR and between d7f5884 and c294fac.

📒 Files selected for processing (5)
  • src/app/movers/[moverId]/page.tsx
  • src/components/common/DetailHeroBanner.tsx
  • src/components/mover/detail/MoverDetailActions.tsx
  • src/components/mover/detail/MoverDetailView.tsx
  • src/hooks/useMoverDesignation.ts

Comment thread src/components/mover/detail/MoverDetailView.tsx
- 리뷰 페이지가 로드된 뒤 300ms 후 다음 페이지 한 장을 자동 prefetch

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

👍 좋았던 점

  • 서버에서 prefetch한 기사님 상세 데이터를 initialDetail로 전달해 인증 초기화 중에도 기본 상세 정보를 먼저 렌더링하도록 한 점이 좋았습니다.
  • HydrationBoundary의 캐시만 사용하는 것이 아니라 초기 렌더링에 필요한 상세 데이터를 직접 전달해 LCP 대상 콘텐츠가 빠르게 노출될 수 있도록 구성한 점이 좋았습니다.
  • 초기에는 서버에서 받은 상세 데이터를 사용하고, 사용자별 Query가 준비되면 기존 queryDetail로 전환하도록 해 기존 TanStack Query 흐름을 유지한 점도 좋았습니다.
  • 히어로 프로필 이미지에만 선택적으로 preload를 적용해 LCP 개선 대상 외의 이미지까지 불필요하게 우선 로드하지 않도록 한 점이 적절해 보입니다.
  • 인증 상태뿐 아니라 고객의 활성 견적 요청 조회까지 완료된 뒤 CTA 상태를 확정하도록 해, 스켈레톤 이후 비활성 버튼이 잠깐 노출됐다가 활성화되는 UI 전환을 방지한 점이 좋았습니다.
  • 사용자별 찜 상태가 확정되기 전에는 찜 액션을 노출하지 않아 guest 기준 상태가 먼저 보였다가 변경되는 플리커도 방지할 수 있을 것으로 보입니다.
  • 데스크톱 CTA 로딩 상태에서도 서버에서 이미 확인된 기사님 이름은 바로 표시하고 버튼 영역만 스켈레톤으로 처리해 불필요한 로딩 UI 범위를 줄인 점이 좋았습니다.
  • CTA 스켈레톤이 실제 버튼 영역과 유사한 공간을 확보하도록 구성되어 인증 및 활성 견적 조회 완료 후 발생하는 레이아웃 변화를 줄이는 목적과 잘 맞습니다.
  • 현재 리뷰 페이지가 로드된 뒤 300ms 후 다음 페이지를 자동 prefetch하면서 기존 hover/focus prefetch도 유지해, 사용자의 이동 시점에 따라 캐시 또는 진행 중인 요청을 재사용할 수 있도록 한 점이 좋았습니다.
  • LCP 측정도 동일한 Slow 4G 환경에서 수정 전/후를 비교해 실제 성능 개선 효과를 함께 확인한 점이 좋았습니다.

🔍 확인 및 제안

1. 초기 guest 상세 → 사용자별 상세 전환

initialDetail은 guest scope의 상세 데이터이고 이후 인증 상태가 확인되면 사용자별 상세 Query 결과로 전환되는 구조로 확인했습니다.

찜이나 CTA처럼 사용자별 상태가 필요한 영역은 Query가 준비되기 전까지 별도로 숨기거나 스켈레톤을 노출하고 있어 guest 상태가 잘못 노출되는 문제는 잘 방지되어 있는 것으로 보입니다.

일반 공개 상세 정보가 guest/authenticated 간 동일하다는 현재 API 기준에서는 적절한 구조로 보입니다.

2. 리뷰 자동 prefetch

현재 페이지가 정상적으로 로드된 뒤 300ms 후 다음 페이지를 자동 prefetch하고 기존 hover/focus prefetch도 유지하는 구조로 확인했습니다.

동일한 query key를 사용하고 있어 이미 캐시가 존재하거나 요청이 진행 중인 경우 TanStack Query에서 이를 재사용할 수 있으므로 현재 구조에서는 적절해 보입니다.

추후 모바일 환경에서 네트워크 요청량을 더욱 엄격하게 관리해야 하는 요구사항이 생긴다면 자동 prefetch 범위를 조정하는 정도는 검토할 수 있을 것 같습니다.

3. isAuthPending 반환값

useMoverDesignation에서 isActionsLoading과 함께 isAuthPending도 반환하도록 변경된 부분이 확인됩니다.

현재 호출부에서 isAuthPending을 별도로 사용하지 않는다면 훅 외부에 노출해야 하는 값인지 한 번 확인해도 좋을 것 같습니다.

내부 isActionsLoading 계산에만 필요하다면 반환값에서는 제외해도 될 것으로 보이며, 기능상 문제는 아니므로 blocker는 아닙니다.

💬 To Reviewer

서버 prefetch 상세 데이터와 기존 TanStack Query의 사용자별 상세 데이터 전환 흐름을 중점적으로 확인했습니다.

초기에는 서버에서 확보한 guest 상세를 initialDetail로 사용하고,
사용자별 Query가 준비되면 기존 queryDetail로 전환하도록 구성되어 있어
LCP 개선을 위한 초기 렌더링과 이후 실제 사용자 상태 반영이 잘 분리되어 있는 것으로 보입니다.

특히 사용자별 상태가 중요한 찜 액션과 CTA는
인증 상태 및 활성 견적 요청 조회가 완료되기 전까지 스켈레톤 또는 비노출 상태로 유지하고 있어,
guest 기준 상태나 임시 비활성 버튼이 먼저 노출되는 문제를 잘 방지한 것으로 확인했습니다.

히어로 프로필 이미지 역시 상세 화면의 LCP 후보 이미지에만 preload를 적용해
다른 이미지까지 불필요하게 우선 로드하지 않은 점이 적절해 보입니다.

리뷰 다음 페이지 prefetch도 현재 페이지 렌더 이후 300ms delayed prefetch와 기존 hover/focus prefetch를 함께 사용하고 있어,
페이지 이동 시 캐시 또는 진행 중인 요청을 재사용할 수 있는 구조로 확인했습니다.

Comment thread src/hooks/useMoverDesignation.ts Outdated
@wndnjs2037

wndnjs2037 commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

page.tsx에서 서버가 prefetch한 상세 데이터를 getQueryData로 꺼내서 initialDetail로 클라이언트에 넘기고, MoverDetailView에서는 인증 확인이 끝나기 전까지 이 initialDetail을 화면에 그리다가 확인이 끝나면 queryDetail로 변경하는 구조로 잘 짜주신 것 같아요.
그 덕분에 인증 상태를 기다리는 동안 전체를 스켈레톤으로 가리는 대신, 서버가 이미 받아온 실제 상세 내용이 바로 보여서 LCP가 크게 줄어든 것 같습니다.

질문 주신 내용 답변드립니다.
1. 자동 prefetch 자체가 적절한지
적절해 보입니다. 리뷰 목록은 로그인 여부와 무관한 공개 데이터라 미리 받아둬도 낭비되는 개인화 요청이 아니고, 300ms 지연을 둬서 초기 LCP 리소스와 겹치지 않게 타이밍도 잡으셔서 타이머도 언마운트 시 clearTimeout으로 정리되니 남는 요청도 없을 것 같아요.

2. 상호작용 있을 때만 하는 게 나은지
지금처럼 자동 prefetch를 같이 두는 쪽이 좋을 것 같습니다.
hover/focus만 남기면 모바일처럼 hover가 없는 환경에서는 prefetch 혜택이 아예 사라지기 때문입니다. 지금 구조는 hover/focus와 자동 prefetch가 같은 함수, 같은 쿼리키를 쓰고 있어서 어느 쪽이 먼저 실행되든 나머지는 캐시를 그대로 재사용합니다. 그래서 상호작용 전용으로 좁히진 않아도 될 것 같습니다.

3. 페이지 한 개만 prefetch하는 범위가 적절한지
지금 규모에서는 적절하다고 생각됩니다.
범위를 넓힌다면 기준은 "미리 받은 페이지가 실제로 얼마나 클릭으로 이어지는가"가 기준이 되면 될 것 같습니다.
2페이지 이동 비율이 낮은데 3페이지까지 미리 받으면 그만큼 안 쓰이는 요청만 늘어나게 되니, 이 점 유의해서 체크하시면 될 거 같아요.

4. 성능 개선 효과 검증용 도구
지금처럼 조건을 고정해서 재는 건 랩 측정입니다. Lighthouse나 DevTools Performance가 여기 속하고, 회귀를 잡는 데 적합합니다.
그리고 실제 사용자 기기/네트워크에서 나온 값은 필드 측정을 해야하는데, 이 프로젝트는 Vercel에 배포되고 있어서 Vercel Speed Insights를 붙이면 실사용자 기준 LCP/INP/CLS를 볼 수 있고, 규모가 커지면 PageSpeed Insights의 CrUX 데이터도 참고할 수 있을 것 같아요.

지표는 LCP만이 아니라 CLS(Cumulative Layout Shift)도 같이 보면 좋을 것 같습니다.
이번 PR에서 작업해주신 CTA 스켈레톤 작업은 레이아웃 이동을 줄이도록 개선하신 것 같은데, CLS를 측정하여 효과를 확인할 수 있습니다.
랩 측정은 PR마다 회귀 여부를 확인하는 용도로, 필드 측정은 배포 후 실사용자 값의 추이를 지켜보는 용도로 나눠서 쓰시면 될 것 같아요.

고생 많으셨습니다 👍

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.

3 participants