[FEAT] 프로필 미완료 시 Guard 설정 변경 - #71
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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 (8)
🚧 Files skipped from review as they are similar to previous changes (7)
📝 WalkthroughWalkthrough인증 게이트와 보호 레이아웃에 Changes프로필 완료 검증 기반
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant AuthGate
participant ProfileCompletionGuard
participant ProfileRequiredModal
participant Router
User->>AuthGate: 보호 페이지 접근
AuthGate->>ProfileCompletionGuard: 인증된 콘텐츠 전달
ProfileCompletionGuard->>ProfileRequiredModal: 미완료 프로필 모달 표시
ProfileRequiredModal->>Router: router.replace(profileCreatePath)
Router-->>User: 프로필 생성 페이지 표시
Possibly related PRs
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/stores/useAuthStore.ts (1)
255-269: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winJWT 힌트 복구 전에
userId를 검증하세요.
userId가 없으면resolveAuthUserFromTokenHint()가id: ""인 사용자를 반환합니다. 그러면 프로필 상태 쿼리가 비활성화되고ProfileCompletionGuard의isStatusPending이 계속true가 될 수 있습니다.userId와role이 모두 유효할 때만 힌트 사용자를 복구하고,userId누락 회귀 테스트를 추가하세요.🤖 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/stores/useAuthStore.ts` around lines 255 - 269, Validate the result of resolveAuthUserFromTokenHint before passing it to setAuthenticatedUser in the token recovery branch. Only restore the hinted user when both userId and role are present and valid; otherwise continue with the existing fallback state, and add a regression test covering a missing userId.
🤖 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/app/estimate-request/page.tsx`:
- Around line 1-10: EstimateRequestPage에 페이지별 metadata export를 추가하고, 이 견적 요청 경로에
맞는 제목과 설명을 정의하세요. 기존 CustomerAuthGate 및 렌더링 구조는 변경하지 말고, 프로젝트의 기존 Metadata 타입 또는
메타데이터 작성 관례를 재사용하세요.
In `@src/components/common/Header/Header.tsx`:
- Around line 161-168: Update Header’s profile-navigation gating around
useProfileCompletionState and the related GNB, SideNav, and dropdown rendering
paths. Combine isStatusPending, hasHydrated, and isCheckingAuth into a separate
restricted-state condition, and hide profile links and avoid rendering
completedProfileMenuItems until authentication hydration and profile-status
checks are complete. Preserve the existing incomplete-profile menu behavior once
the state is known.
---
Outside diff comments:
In `@src/stores/useAuthStore.ts`:
- Around line 255-269: Validate the result of resolveAuthUserFromTokenHint
before passing it to setAuthenticatedUser in the token recovery branch. Only
restore the hinted user when both userId and role are present and valid;
otherwise continue with the existing fallback state, and add a regression test
covering a missing userId.
🪄 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 Plus
Run ID: c986d8b7-8bf8-4486-9dfc-2fedaca33eeb
📒 Files selected for processing (20)
src/app/(customer)/(protected)/layout.tsxsrc/app/(mover)/(protected)/layout.tsxsrc/app/estimate-request/page.tsxsrc/app/estimate/received-requests/page.tsxsrc/app/estimate/sent/[estimateId]/page.tsxsrc/app/estimate/sent/page.tsxsrc/components/auth/CustomerAuthGate.tsxsrc/components/auth/MoverAuthGate.tsxsrc/components/auth/ProfileCompletionGuard.tsxsrc/components/common/Header/Header.tsxsrc/components/common/Header/HeaderSideNav.tsxsrc/components/common/Modal/AlertModal.tsxsrc/components/profile/CustomerProfileEditView.tsxsrc/components/profile/MoverBasicInfoEditView.tsxsrc/components/profile/MoverProfileEditView.tsxsrc/components/profile/ProfileRequiredModal.tsxsrc/hooks/profile/useProfileCompletionState.tssrc/lib/auth/redirect.tssrc/lib/profile/isProfileIncomplete.tssrc/stores/useAuthStore.ts
There was a problem hiding this comment.
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/profile/useProfileCompletionState.ts`:
- Around line 41-48: 인증되었지만 역할이 없는 상태를 미확정으로 처리하고 기본 고객용 UI를 렌더링하지 않도록 수정하세요.
src/hooks/profile/useProfileCompletionState.ts의 isAuthPending 및
isCompletionUnresolved 계산에 인증 상태의 빈 role 조건을 포함하세요.
src/components/common/Header/Header.tsx의 resolvedRole 기반 네비게이션과 프로필 메뉴는 역할이 없을 때
숨기거나 로그아웃 메뉴만 표시하도록 변경하고, 고객용 기본값을 사용하지 마세요.
🪄 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 Plus
Run ID: 3d22366e-e705-4d39-b82d-206f7ccf0533
📒 Files selected for processing (2)
src/components/common/Header/Header.tsxsrc/hooks/profile/useProfileCompletionState.ts
juengseulki
left a comment
There was a problem hiding this comment.
📋PR 리뷰
👍 좋았던 점
- 인증 여부와 프로필 완료 여부를 별도 상태로 분리해, 로그인은 완료됐지만 프로필이 없는 사용자를 정상적인 인증 사용자로 유지했습니다.
- Customer와 Mover의 프로필 상태 조회 및 생성 경로 계산을
useProfileCompletionState로 공통화했습니다. - Zustand Hydration,
checkAuth, Profile Status 조회가 끝나기 전 상태를isCompletionUnresolved로 분리해 Header 메뉴 깜빡임을 방지했습니다. isProfileCompleted === false와 프로필 없음 오류만 미완료로 판단하고, 네트워크·5xx 오류에서는 Fail-open 정책을 적용했습니다.- 공개 페이지는 그대로 탐색할 수 있게 두고 실제 보호 기능에서만 프로필 생성 안내를 제공해 Soft UX Guard의 목적에 잘 맞았습니다.
- 고객과 기사 역할별 프로필 생성 경로를 Allowlist로 처리해 생성 화면 자체가 Guard에 막히지 않도록 했습니다.
- 프로필 미완료 사용자가 보호 기능에 접근했을 때 강제로 이동시키기보다, 접근이 제한된 이유와 프로필 생성 CTA를 모달로 안내했습니다.
- Header GNB와 SideNav에서도 미완료 상태를 반영해 사용할 수 없는 메뉴를 감추고, 모바일에서는 안내 문구를 제공했습니다.
- 프로필 완료 여부가 아직 확정되지 않은 상태에서는 완료 사용자 메뉴를 노출하지 않아 초기 렌더링의 잘못된 UI 노출을 방지했습니다.
/me가 프로필 미생성으로 실패하더라도 Access Token의 힌트로 사용자 역할과 ID를 복원해 AuthGate 및 Profile Guard가 동작하도록 보완했습니다.- 401·403은 기존대로 인증 종료 처리해 실제 인증 실패와 프로필 미생성·일시 오류를 구분했습니다.
AlertModal.onClose를 Optional로 변경하면서 닫기 버튼과 Modal 종료 콜백도 함께 조건부 처리해 기존 사용처의 동작을 유지했습니다.
🔍 확인 및 제안
필수 수정사항은 보이지 않았습니다.
프로필 없음 여부를 판별할 때 404와 NOT_FOUND 외에
오류 메시지의 "등록된 프로필" 문자열도 사용하고 있습니다.
현재 백엔드 오류 코드가 완전히 통일되지 않은 상황을 보완하기에는 유용하지만,
향후에는 고객·기사 프로필 없음 전용 Error Code를 추가하고
문자열 의존을 제거하는 것이 더 안전해 보입니다.
또한 ProfileCompletionGuard가 (protected)/layout과
CustomerAuthGate·MoverAuthGate 양쪽에서 사용될 수 있습니다.
현재 구조는 누락 방지에는 안전하지만,
동일한 페이지에서 중복 적용되지 않도록 아래와 같은 기준을 유지하면 좋겠습니다.
- Route Group 내부 보호 페이지는
(protected)/layout에서 처리 - Route Group 밖의 개별 보호 페이지는 AuthGate에서 처리
JWT 힌트로 복원한 user는 Client UI와 Guard를 동작시키기 위한 보조 상태로만 사용하고,
실제 권한 검증은 계속 백엔드의 Access Token 및 역할 검증에 맡기는 현재 원칙을 유지하면 충분해 보입니다.
전체적으로 프로필 미완료 상태를 단순 로그아웃이나 강제 Redirect로 처리하지 않고,
공개 탐색은 허용하면서 필요한 기능에만 부드럽게 제한을 거는 흐름이 잘 구성됐습니다.
현재 변경사항 기준으로 병합을 막을 만한 문제는 보이지 않습니다.
수고하셨습니다! 😊
To Reviewer 내용 기준으로 프로필 미생성 시 UI 흐름,
Guard 적용 위치와 AlertModal 변경 영향을 중점적으로 확인했습니다!
프로필 미생성 시 현재 UI 이동 방식은 적절해 보입니다.
공개 페이지는 계속 탐색할 수 있게 하고,
실제 프로필이 필요한 보호 기능에 접근했을 때만
닫을 수 없는 안내 모달과 프로필 생성 CTA를 제공하고 있습니다.
무조건 프로필 생성 페이지로 Redirect하는 방식보다
사용자가 현재 기능을 이용할 수 없는 이유를 이해할 수 있고,
다음 행동도 명확하게 안내받을 수 있어 Soft UX Guard 목적에 잘 맞습니다.
Header와 SideNav에서도 프로필 미완료 상태에 사용할 수 없는 메뉴를 숨기고,
프로필 생성과 로그아웃만 제공하는 흐름도 자연스럽습니다.
추가로 isCompletionUnresolved를 통해
Zustand Hydration, checkAuth, Profile Status 조회가 완료되기 전에는
완료 사용자용 메뉴와 GNB를 노출하지 않도록 처리해
초기 렌더링 시 메뉴가 잠깐 보였다 사라지는 문제도 잘 보완된 것으로 확인했습니다.
Guard를 (protected)/layout과
CustomerAuthGate·MoverAuthGate에 각각 둔 구조도 현재 기능상 문제는 없어 보입니다.
React Query가 동일한 Profile Status Query Key를 사용하므로
Guard가 겹치더라도 실제 네트워크 요청은 공유될 가능성이 큽니다.
다만 장기적으로는 다음 기준으로 책임을 구분하면 더 명확할 것 같습니다.
(protected)Route Group 내부 페이지는 Layout에서 Profile Guard 적용- Route Group 밖에서 AuthGate를 직접 사용하는 페이지는 AuthGate에서 Profile Guard 적용
이 기준을 유지하면 보호 누락을 막으면서도 동일 페이지에 Guard가 중복 적용되는 구조를 줄일 수 있습니다.
AlertModal.onClose를 Optional로 변경한 부분도 적절합니다.
onClose가 전달된 기존 사용처에서는 기존과 동일하게 닫기 버튼과 종료 동작이 유지되고,
onClose가 없는 경우에는 닫기 버튼을 렌더링하지 않으며
하위 Modal에도 닫기 Handler를 전달하지 않습니다.
따라서 기존 모달 동작에 영향을 크게 주지 않으면서,
ProfileRequiredModal처럼 사용자가 닫을 수 없어야 하는 사례를 지원하도록 확장된 형태입니다.
checkAuth에서 /me 실패 시 JWT 힌트로 user를 복원하는 처리도
프로필 미생성 사용자를 로그아웃시키지 않고
AuthGate와 역할별 프로필 생성 흐름을 유지하기 위한 목적에 적절합니다.
다만 JWT 힌트는 Client 화면 상태 복원 용도로만 사용하고,
실제 API 접근 권한은 계속 백엔드 Access Token과 역할 검증에 맡기는 현재 원칙을 유지하면 좋겠습니다.
There was a problem hiding this comment.
작업하느라 수고하셨습니다! 프로필 미생성 시 가드도 잘 작동하며, 헤더 부분에도 프로필 미등록 시 처리를 구현하신 점이 좋았습니다. 또한 기존 공통 컴포넌트 AlertModal를 활용하신 점도 좋았습니다. onClose를 optional로 바꾸었기 때문에 다른 기존 사용처에 지장이 없을 것으로 보입니다.
프로필 미등록 시 바로 등록 페이지로 이동하거나 프로필 등록을 안내하는 별도의 페이지로 이동하는 대신, 모달을 띄우는 방식은 사용자 입장에서 왜 현재 동작이 막혔는지 알 수 있으면서도 URL·라우트·로딩이 한 번 더 생기는 비효율이 발생하지 않는 이점이 있다고 보았습니다! 현재 정책을 그대로 유지해도 좋을 것 같습니다.
다만 현재 pr 범위엔 살짝 벗어나지만 한 가지 의문이 생겨서 인라인 코멘트로 남겨두었습니다!
…into feature/profile-guard
| useProfileCompletionState(resolvedRole); | ||
|
|
||
| // SSR 로그인 힌트와 status 확정 전: 완료 사용자 메뉴/링크가 깜빡이지 않도록 숨김 | ||
| const shouldHideNavLinks = isLogin && (isIncomplete || isCompletionUnresolved); |
There was a problem hiding this comment.
프로필이 완료된 사용자도 새로고침 직후에는 GNB와 사이드 링크가 잠깐 비어 보일 수 있을 것 같습니다. isCompletionUnresolved가 hydrate, checkAuth, status 조회가 끝날 때까지 true인데 isLogin은 SSR 힌트 기반이라 첫 페인트부터 true이고, showAuthSkeleton도 우측 프로필 영역만 가리고 있습니다. 링크가 늦게 뜨면서 화면이 밀리지 않는지 한번 확인해보시면 좋을 것 같습니다!
문제가 된다면 드롭다운 메뉴만 확정 후 노출하고 링크는 먼저 보여주거나, 숨기는 대신 스켈레톤으로 자리를 잡아주는 방향이 있을 것 같아요.
| return ( | ||
| <> | ||
| {loadingFallback} | ||
| <ProfileRequiredModal open profileCreatePath={profileCreatePath} /> | ||
| </> |
There was a problem hiding this comment.
차단 상태에서 배경으로 loadingFallback을 그대로 쓰고 있는데, AuthGate 경로에서는 이 값이 "견적 요청을 준비하는 중입니다." 같은 로딩 문구라 실제로는 차단인데 준비 중처럼 보일 수 있을 것 같습니다. 차단 시에는 중립 배경이나 모달만 두는 것도 좋을 것 같아요 :)
|
개발 고생 많으셨습니다! useProfileCompletionState로 Header와 Guard가 같은 파생값과 같은 쿼리 키를 공유하는 구조로 짜주셔서 헤더 메뉴 판정과 라우트 차단 판정이 한 곳에서 나오니 서로 어긋날 일이 없고, status 요청도 자연히 공유되어 중복 호출이 줄어드는 장점이 있을 것 같습니다. To Reviewer 답변드리면, 1. 프로필 미생성 시 UI 이동 방식 2. layout과 AuthGate 양쪽에 Guard 3. AlertModal onClose optional 고생하셨습니다 👍 |
📋 작업 내용
checkAuth에서 프로필 미생성(me 404 등) 시 JWT 힌트로user를 채워 AuthGate·status 가드가 동작하도록 수정🔥 변경 사항
프로필 완료 가드
ProfileCompletionGuard: 보호 라우트에서 미완료 시ProfileRequiredModal표시 (닫기 불가, 생성 경로로 이동)useProfileCompletionState로 Gate/Guard 공통 상태 관리isProfileIncomplete로 statusfalse/ 프로필 없음(404 등) 판별, 그 외 오류는 실패해도 페이지는 보여줌 (404 제외 에러)/profile,/mover/profile/,/movers등)는 가드하지 않음인증·공통
useAuthStore.checkAuth: 토큰 있고 me 실패(401/403 제외) 시resolveAuthUserFromTokenHint로 세션 유지AlertModal:onCloseoptional 변경✅ 체크리스트
📷 스크린샷 (선택)
Header및SideNavModal💬 To Reviewer
(protected)/layout과CustomerAuthGate/MoverAuthGate에 각각 걸려 있는데, 이 구조가 괜찮은지 확인 부탁드립니다.AlertModal의 onClose 를 optional 로 바꾸었는데 다른 부분에 지장이 없을지 확인 부탁드립니다.Summary by CodeRabbit
새로운 기능
개선 사항