[feat] 마이페이지 API 연동 및 누락된 UI 추가#42
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough스코프 기반 사용자 API와 공용 파일 업로드 모듈을 도입하고, 닉네임/비밀번호/이메일/소셜/탈퇴 관련 타입·API·뮤테이션 훅·UI 페이지와 라우팅을 추가하여 마이페이지 프로필 관리를 확장합니다. Changes사용자 프로필 관리 기능
🎯 4 (Complex) | ⏱️ ~45 minutes Suggested Reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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/features/user/me/hooks/useUserMeMutations.ts`:
- Around line 24-26: The current useCurrentUserScope() forcibly falls back to
'USER', risking wrong endpoints while scope is unresolved; change
useCurrentUserScope (and any callers of it) to NOT default to 'USER'—either
return undefined/null when useAuthStore(state => state.scope) is missing or
throw a clear error from useCurrentUserScope() so mutations (e.g., user-me
mutation hooks) are blocked until scope is resolved; update callers to check the
returned value and prevent or abort the mutation rather than sending requests to
the wrong endpoint.
In `@src/pages/my/profile/email/index.tsx`:
- Around line 20-105: The page currently owns the entire email verification
state machine (sessionId, email, code, message, successMessage) and the action
handlers (handleSend, handleVerify, handleUpdate, handleDelete) which breaks
layering; extract this workflow into a feature hook (e.g. useEmailVerification
or useEmailEmailFlow) that imports the existing mutations
(useSendEmailVerificationMutation, useVerifyEmailCodeMutation,
useUpdateEmailMutation, useDeleteEmailMutation) and encapsulates all state
(email, code, sessionId, message, successMessage), derived booleans (isPending,
canSend, canVerify, canUpdate, canDelete, trimmedEmail) and the methods
clearMessages/send/verify/update/delete; have the hook return the state,
booleans, and action functions so the page component only composes UI and calls
those returned functions (replace uses of sessionId, handleSend, handleVerify,
handleUpdate, handleDelete, clearMessages, message, successMessage, isPending,
canSend, canVerify, canUpdate, canDelete in the page).
In `@src/pages/my/profile/index.tsx`:
- Around line 100-161: ProfileEditPage contains file upload/delete business
logic (handlers handleAvatarUpload, handleAvatarFileChange,
handleOpenDeleteModal, handleDeleteAvatar), mutation usage
(useUpdateProfileImageMutation, useDeleteProfileImageMutation), uploadAppFile
calls and error state (imageError, deleteModalOpen) that should be moved to a
feature hook; extract this into a new hook (e.g., useProfileImageEditor in
src/features/user/me) that encapsulates state (imageError, isImagePending,
deleteModalOpen), side effects (calling uploadAppFile, mutateAsync on
update/delete mutations), and handlers (triggerFileInput, onFileChange,
openDeleteModal, confirmDelete) and returns those values/handlers; then simplify
ProfileEditPage to only call useProfileImageEditor and wire returned handlers to
UI, removing direct API/error logic from the page.
In `@src/pages/my/profile/nickname/index.tsx`:
- Around line 11-41: Extract the nickname-change business flow from the page
into a feature hook (e.g., useChangeNickname or useUpdateNicknameUseCase): move
validation (trim/length/duplicate check against user.nickname), the call to
useUpdateNicknameMutation (mutateAsync), and error mapping
(getAxiosErrorMessage) into that hook and expose a simple API like { nickname,
setNickname, message, canSubmit, submit } for the page to consume; update the
page to only import useUserMe for initial value and the new hook, render
inputs/button/messages, call submit() and on success let the hook return a
success flag or throw so the page can call navigate(ROUTES.MY.PROFILE, {
replace: true }) — keep references to useUpdateNicknameMutation,
trimmedNickname/canSubmit logic, handleSubmit behavior, and getAxiosErrorMessage
inside the new feature hook.
In `@src/pages/my/profile/password/index.tsx`:
- Around line 10-54: Move the password policy and submission orchestration out
of PasswordEditPage into a new feature hook (e.g., usePasswordUpdateFeature)
that encapsulates isPasswordFormatValid, the submit flow that calls
useUpdatePasswordMutation.mutateAsync, error handling via getAxiosErrorMessage,
and navigation to ROUTES.MY.PROFILE; then have PasswordEditPage only manage
local form state (currentPassword, newPassword, confirmPassword) and call the
hook's handlers (validate, handleSubmit, and canSubmit) instead of containing
isPasswordFormatValid, handleSubmit, and canSubmit logic directly so policy and
mutation orchestration live in the feature layer and the page only composes the
form.
In `@src/pages/my/profile/social/index.tsx`:
- Around line 51-107: Extract the social linking/unlinking logic from the page
into a feature hook (e.g., create useSocialAccountLinking) that encapsulates
state and actions: move handleLink and handleUnlink implementations, state
setters (setMessage, setPendingProvider), and all provider-specific flows
(requestFreshKakaoAuthorizationCode, getKakaoOAuthRedirectUri, loginWithApple)
plus the mutations (linkSocialAccountMutation, unlinkSocialAccountMutation) and
error handling (getAxiosErrorMessage) into that hook; have the hook expose:
pendingProvider, message, link(provider: SocialProvider) and unlink(provider:
SocialProvider) so the page keeps only rendering PROVIDERS and wiring onClick to
link/unlink, and remove any business logic from
src/pages/my/profile/social/index.tsx.
In `@src/pages/my/withdraw/index.tsx`:
- Around line 18-39: Move the entire withdrawal flow out of the page component
into a new hook (e.g., useWithdrawUser) so the page only binds UI state and
calls an action; the hook should accept dependencies (withdrawUserMutation,
logout, navigate, scope) or internally import them, expose a performWithdraw()
async function and any loading/error state, and encapsulate confirmation,
mutateAsync call, logout, navigation to ROUTES.AUTH.LOGIN, and error fallback
via getAxiosErrorMessage. Replace handleWithdraw in the page with a simple call
to performWithdraw() and keep checked/setMessage only for UI state; ensure the
hook returns errors/messages so the page can display setMessage without
containing business logic.
🪄 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
Run ID: 9f863a58-cd2b-42d3-b7b0-44e1f2c05d84
📒 Files selected for processing (15)
src/app/App.tsxsrc/features/user/me/api/user.tssrc/features/user/me/hooks/useUserMe.tssrc/features/user/me/hooks/useUserMeMutations.tssrc/features/user/me/index.tssrc/features/user/me/types/user.tssrc/pages/my/index.tsxsrc/pages/my/profile/email/index.tsxsrc/pages/my/profile/index.tsxsrc/pages/my/profile/nickname/index.tsxsrc/pages/my/profile/password/index.tsxsrc/pages/my/profile/social/index.tsxsrc/pages/my/withdraw/index.tsxsrc/shared/constants/routes.tssrc/shared/lib/queryKeys.ts
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/features/user/me/hooks/useEmailVerificationFlow.ts`:
- Line 20: The local email state (const [email, setEmailState] =
useState(currentEmail)) isn't synced when currentEmail updates; add an effect in
useEmailVerificationFlow.ts that watches currentEmail and calls
setEmailState(currentEmail) when it changes (ensure you guard against
undefined/null if needed) so the controlled input represented by email stays in
sync with updates to currentEmail.
In `@src/features/user/me/hooks/usePasswordUpdateFeature.ts`:
- Around line 8-13: The password validation uses trimmed = value.trim() but the
request still sends the original value, causing mismatch for inputs with
surrounding spaces; update usePasswordUpdateFeature to use a single canonical
string for both validation and transmission (e.g., replace uses of raw value
with the trimmed variable when performing the length/regex checks and when
packaging the password to send), and apply the same change to the other
occurrence referenced (lines 47-48) so validation and network submission use the
identical string.
In `@src/features/user/me/hooks/useProfileImageEditor.ts`:
- Line 2: The import of uploadAppFile from
"`@/features/store-register/api/workspaceFileUpload`" inside useProfileImageEditor
(src/features/user/me/hooks/useProfileImageEditor.ts) breaks feature-layer
boundaries; move the upload API into the entities or shared layer (or add a
passthrough module in entities/shared) and update useProfileImageEditor to
import from that new module instead of the store-register feature; ensure the
symbol name uploadAppFile is preserved (or export a renamed facade) and update
any related imports/exports and tests/build configs that reference
workspaceFileUpload so the features layer only imports from entities/shared.
🪄 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
Run ID: 7ff37cbf-47d0-4d88-a40e-6f7944ed2868
📒 Files selected for processing (15)
src/features/user/me/hooks/useChangeNickname.tssrc/features/user/me/hooks/useEmailVerificationFlow.tssrc/features/user/me/hooks/usePasswordUpdateFeature.tssrc/features/user/me/hooks/useProfileImageEditor.tssrc/features/user/me/hooks/useSocialAccountLinking.tssrc/features/user/me/hooks/useUserMe.tssrc/features/user/me/hooks/useUserMeMutations.tssrc/features/user/me/hooks/useWithdrawUserFlow.tssrc/features/user/me/index.tssrc/pages/my/profile/email/index.tsxsrc/pages/my/profile/index.tsxsrc/pages/my/profile/nickname/index.tsxsrc/pages/my/profile/password/index.tsxsrc/pages/my/profile/social/index.tsxsrc/pages/my/withdraw/index.tsx
|
@CodeRabbit 리뷰 또 해줘 |
|
(ˆ•ω•ˆ) ✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/app/App.tsx (1)
31-37: ⚡ Quick win프로필 관련 페이지들을 lazy loading으로 전환하여 초기 번들 크기를 최적화하세요.
현재
MyPage및 프로필 하위 5개 페이지(EmailEditPage,NicknameEditPage,PasswordEditPage,SocialAccountPage,WithdrawPage)가 모두 직접 import되어 초기 번들에 포함됩니다. 이들은 사용 빈도가 낮아 lazy loading 적용 시 초기 로딩 성능을 개선할 수 있습니다.♻️ lazy loading 적용 예시
기존 직접 import를 제거하고:
-import { MyPage } from '`@/pages/my`' -import { ProfileEditPage } from '`@/pages/my/profile`' -import { EmailEditPage } from '`@/pages/my/profile/email`' -import { NicknameEditPage } from '`@/pages/my/profile/nickname`' -import { PasswordEditPage } from '`@/pages/my/profile/password`' -import { SocialAccountPage } from '`@/pages/my/profile/social`' -import { WithdrawPage } from '`@/pages/my/withdraw`'lazy import로 변경:
+const MyPage = lazy(async () => { + const m = await import('`@/pages/my`') + return { default: m.MyPage } +}) + +const ProfileEditPage = lazy(async () => { + const m = await import('`@/pages/my/profile`') + return { default: m.ProfileEditPage } +}) + +const EmailEditPage = lazy(async () => { + const m = await import('`@/pages/my/profile/email`') + return { default: m.EmailEditPage } +}) + +const NicknameEditPage = lazy(async () => { + const m = await import('`@/pages/my/profile/nickname`') + return { default: m.NicknameEditPage } +}) + +const PasswordEditPage = lazy(async () => { + const m = await import('`@/pages/my/profile/password`') + return { default: m.PasswordEditPage } +}) + +const SocialAccountPage = lazy(async () => { + const m = await import('`@/pages/my/profile/social`') + return { default: m.SocialAccountPage } +}) + +const WithdrawPage = lazy(async () => { + const m = await import('`@/pages/my/withdraw`') + return { default: m.WithdrawPage } +})각 라우트에 Suspense 적용:
- <Route path={ROUTES.MY.ROOT} element={<MyPage />} /> + <Route + path={ROUTES.MY.ROOT} + element={ + <Suspense fallback={null}> + <MyPage /> + </Suspense> + } + />나머지 프로필 관련 라우트들도 동일하게 Suspense로 감싸주세요.
Also applies to: 112-125, 186-186
🤖 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/app/App.tsx` around lines 31 - 37, Replace the direct imports of MyPage and its profile subpages (MyPage, ProfileEditPage, EmailEditPage, NicknameEditPage, PasswordEditPage, SocialAccountPage, WithdrawPage) with React.lazy dynamic imports and ensure each route using these components is wrapped with React.Suspense (provide a fallback) so they load only when visited; update any route definitions or parent components that render ProfileEditPage and the five subpages to use the lazy-loaded components and Suspense boundaries (keep existing export/names intact so route references to MyPage, ProfileEditPage, EmailEditPage, NicknameEditPage, PasswordEditPage, SocialAccountPage, WithdrawPage continue to work).
🤖 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/features/user/me/hooks/useProfileImageEditor.ts`:
- Around line 16-17: The isImagePending flag currently only includes
updateProfileImageMutation.isPending and deleteProfileImageMutation.isPending,
so include the upload pending state from uploadAppFile (or the mutation/wrapper
that performs file uploads) in that boolean to block actions during upload;
update the isImagePending expression in useProfileImageEditor (and any other
similar checks in the same file around lines 24-41) to OR in the upload
mutation's isPending/awaiting state (e.g., uploadAppFileMutation.isPending or
uploadAppFile.isPending) so delete/retry/update actions are disabled while an
upload is in progress.
In `@src/features/user/me/hooks/useWithdrawUserFlow.ts`:
- Around line 15-24: Add a re-entry guard inside performWithdraw to prevent
duplicate destructive requests by checking the mutation/loading state (e.g.
withdrawUserMutation.isLoading) or a local isPending flag at the very start of
performWithdraw and returning early if already pending; ensure this check runs
before showing the confirmation prompt and/or before calling
withdrawUserMutation.mutateAsync(), so performWithdraw, withdrawUserMutation,
and any new isPending state are used to block concurrent invocations.
---
Nitpick comments:
In `@src/app/App.tsx`:
- Around line 31-37: Replace the direct imports of MyPage and its profile
subpages (MyPage, ProfileEditPage, EmailEditPage, NicknameEditPage,
PasswordEditPage, SocialAccountPage, WithdrawPage) with React.lazy dynamic
imports and ensure each route using these components is wrapped with
React.Suspense (provide a fallback) so they load only when visited; update any
route definitions or parent components that render ProfileEditPage and the five
subpages to use the lazy-loaded components and Suspense boundaries (keep
existing export/names intact so route references to MyPage, ProfileEditPage,
EmailEditPage, NicknameEditPage, PasswordEditPage, SocialAccountPage,
WithdrawPage continue to work).
🪄 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
Run ID: abc8fc9c-521f-41d3-9a74-b1c48b1eefbd
📒 Files selected for processing (23)
src/app/App.tsxsrc/features/store-register/api/workspaceFileUpload.tssrc/features/user/me/api/user.tssrc/features/user/me/hooks/useChangeNickname.tssrc/features/user/me/hooks/useEmailVerificationFlow.tssrc/features/user/me/hooks/usePasswordUpdateFeature.tssrc/features/user/me/hooks/useProfileImageEditor.tssrc/features/user/me/hooks/useSocialAccountLinking.tssrc/features/user/me/hooks/useUserMe.tssrc/features/user/me/hooks/useUserMeMutations.tssrc/features/user/me/hooks/useWithdrawUserFlow.tssrc/features/user/me/index.tssrc/features/user/me/types/user.tssrc/pages/my/index.tsxsrc/pages/my/profile/email/index.tsxsrc/pages/my/profile/index.tsxsrc/pages/my/profile/nickname/index.tsxsrc/pages/my/profile/password/index.tsxsrc/pages/my/profile/social/index.tsxsrc/pages/my/withdraw/index.tsxsrc/shared/api/appFileUpload.tssrc/shared/constants/routes.tssrc/shared/lib/queryKeys.ts
limtjdghks
left a comment
There was a problem hiding this comment.
코드래빗은 신이야
드디어 마이페이지 기능이 연동이 되엇군여 회원 탈퇴 기능이 생긴 김에 제 인생도 탈퇴하고싶네요 고생하셨습니다
| function isPasswordFormatValid(value: string): boolean { | ||
| const trimmed = value.trim() | ||
| if (trimmed.length < 8 || trimmed.length > 16) return false | ||
| const hasLetter = /[A-Za-z]/.test(trimmed) | ||
| const hasNumber = /\d/.test(trimmed) | ||
| const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(trimmed) | ||
| return hasLetter && hasNumber && hasSpecial | ||
| } |
There was a problem hiding this comment.
이거 회원가입 할때도 동일한 규칙 사용할 것 같은데 공용 유틸로 빼서 통일하면 좋을 듯 싶습니다
| const validate = (): string => { | ||
| if (!isPasswordFormatValid(newPassword)) { | ||
| return '새 비밀번호는 8~16자, 영문·숫자·특수문자를 모두 포함해야 합니다.' | ||
| } | ||
| if (newPassword !== confirmPassword) { | ||
| return '새 비밀번호가 일치하지 않습니다.' | ||
| } | ||
| return '' | ||
| } |
There was a problem hiding this comment.
얘도 회원가입 페이지에서 사용할 수 있는 것 맞나욧? 얘도 같이 하면 조을 덧
ID
변경 내용
구현 사항
회원 탈퇴는 성공 시 로컬 인증 상태를 정리하고 로그인 화면으로 이동하며, MANAGER scope에서는 운영 중 업장으로 인한 탈퇴 제한 에러를 안내하도록 처리했습니다.
구현 시연 (필요 시)
2026-05-21.9.57.02.mov
참고 사항 (필요 시)
{} 빈 함수)
상태)
매니저 본인 정보 (MANAGER scope)
/manager/me/profile-image
해당 api들 누락 없이 잘 반영된건지 꼼꼼히 살펴서 리뷰해주세요 @CodeRabbit
Summary by CodeRabbit