-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] user 엔드포인트 API 연결 #175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…into chiyoung-fix/mypage-edit
- profile info update - profile image update - notification update
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Walkthrough프로필 관련 API와 훅을 실 API 형태로 재구성하고, 이미지 폴백 컴포넌트 추가 및 프로필 수정의 두 단계 업데이트(프로필 정보 · 이미지)를 도입했으며 알림 토글을 서버 뮤테이션으로 전환했습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor 사용자
participant ProfileModal as 프로필 편집 모달
participant Hook_Update as useUpdateUser
participant Hook_Image as useUserImageUpdate
participant API as UserService API
participant Query as QueryClient
사용자->>ProfileModal: 폼 제출(프로필 데이터 + 이미지)
rect rgb(200,220,255)
Note over ProfileModal,Hook_Update: 단계 1 - 프로필 정보 업데이트
ProfileModal->>Hook_Update: mutate(nextProfileInfo)
Hook_Update->>API: PATCH /users/profile (payload)
API-->>Hook_Update: 업데이트된 User 반환
Hook_Update->>Query: invalidate user queries
end
rect rgb(200,255,220)
Note over ProfileModal,Hook_Image: 단계 2 - 이미지 업로드 (파일 존재 시)
alt 이미지 파일 있음
ProfileModal->>Hook_Image: mutate({ file })
Hook_Image->>API: PATCH /users/profile-image (FormData)
API-->>Hook_Image: 업데이트된 User 반환
Hook_Image->>Query: invalidate user queries
end
end
ProfileModal->>사용자: 모달 닫힘 / UI 갱신
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
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 |
🎭 Playwright Report✨ E2E Test가 성공적으로 완료되었습니다. Test 요약 내용을 확인해주세요.
📊 Test Summary
📜 Test Details✅ Passed Tests (9)
|
🎨 Storybook Report✅ 변경 사항이 없습니다 모든 Story가 이전 빌드와 동일합니다.
|
📊 Coverage Report
📈 #175을 main에 병합하면 coverage가 Coverage 요약@@ Coverage Diff @@
## main #175 +/- ##
===========================================
+ Coverage 35.21% 35.47% +0.26%
===========================================
Files 146 149 +3
Lines 5974 6134 +160
Branches 234 244 +10
===========================================
+ Hits 2104 2176 +72
+ Misses 3870 3958 +88 영향받은 파일
|
🚀 PR Preview Report✨ Build가 성공적으로 완료되었습니다. Preview에서 변경사항을 확인하세요.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/hooks/use-user/use-user-get/index.ts (1)
7-18: The?? ''defaults in theselecttransformer are unnecessary; the API returns non-null strings.The User type and all mock data confirm that
profileImage,profileMessage, andmbtiare always strings from the API. No consumers check for null to distinguish "unset" vs "empty" state; components render these fields directly. The hook can be simplified by removing the select transformer or documenting why it's defensive.
🧹 Nitpick comments (5)
src/components/pages/user/profile/profile-card/index.tsx (1)
13-19: Consider addingsizes(and confirm eager-loading semantics in the wrapper).With
fill, providingsizesavoids suboptimal image selection; here the container issize-24(~96px).<ImageWithFallback className='object-cover' alt='프로필 이미지' fill - loading='eager' + sizes='96px' + loading='eager' src={profileImage} />Also verify whether
ImageWithFallbackmaps tonext/imageand whether you’d rather usepriorityfor above-the-fold avatars.src/components/pages/user/mypage/mypage-setting/index.tsx (1)
11-21: Guard against rapid re-clicks (pending state) when toggling notifications.Consider disabling the button while the mutation is pending (or use optimistic update + rollback). Example:
export const MyPageSetting = ({ user }: Props) => { - const { mutate } = useUpdateMyNotification(); + const { mutate, isPending } = useUpdateMyNotification(); return ( <section className='bg-mono-white flex flex-col gap-3 px-3 py-6'> <MyPageToggleButton value={user.isNotificationEnabled} + disabled={isPending} onClick={() => mutate({ isNotificationEnabled: !user.isNotificationEnabled })} >src/hooks/use-user/use-user-update/index.ts (1)
7-15: Good hook signature change; don’t leaveonErrorempty.At minimum, capture the error for UI/telemetry (even if you don’t toast here).
const query = useMutation({ mutationFn: (payload: UpdateMePayload) => API.userService.updateMe(payload), onSuccess: (data, _variables, _context) => { queryClient.invalidateQueries({ queryKey: userKeys.item(data.userId) }); }, - onError: () => {}, + onError: (error) => { + // TODO: route to centralized logger/toast + console.error(error); + }, });src/components/ui/image-with-fallback/index.tsx (1)
24-26: Consider removingunoptimizedand document the'null'string check.
The
unoptimizedprop disables Next.js image optimization benefits. If this is intentional (e.g., for S3 images already optimized), add a comment explaining why.The
src === 'null'check suggests the API may return a literal"null"string. This is a valid defensive measure, but consider fixing this at the API response transformation layer (e.g., inuseGetUser) for cleaner component logic.- const imgSrc = error || !src || src === 'null' ? fallbackSrc : src; + // Handle cases where API returns "null" as a string instead of null/undefined + const imgSrc = error || !src || src === 'null' ? fallbackSrc : src; - return <Image {...rest} src={imgSrc} unoptimized onError={() => setError(true)} />; + // unoptimized: S3 images are pre-optimized; avoids Next.js re-processing + return <Image {...rest} src={imgSrc} unoptimized onError={() => setError(true)} />;src/hooks/use-user/use-user-image-update/index.ts (1)
7-17: LGTM!The hook correctly follows the established mutation pattern. The empty
onErrorhandler delegates error handling to consumers, which is appropriate since the modal component usesmutateAsyncand can handle errors locally.One optional consideration: you could remove the empty
onErrorcallback entirely since it's the default behavior, or add logging for debugging purposes.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
next.config.ts(1 hunks)src/api/service/user-service/index.ts(1 hunks)src/app/(user)/mypage/page.tsx(0 hunks)src/components/pages/user/mypage/mypage-setting/index.tsx(2 hunks)src/components/pages/user/profile/profile-card/index.tsx(2 hunks)src/components/pages/user/profile/profile-edit-fields/image-field/index.tsx(2 hunks)src/components/pages/user/profile/profile-edit-fields/index.ts(1 hunks)src/components/pages/user/profile/profile-edit-modal/index.tsx(3 hunks)src/components/ui/image-with-fallback/index.tsx(1 hunks)src/components/ui/index.ts(1 hunks)src/hooks/use-user/index.ts(0 hunks)src/hooks/use-user/use-user-get/index.ts(1 hunks)src/hooks/use-user/use-user-image-update/index.ts(1 hunks)src/hooks/use-user/use-user-notification/index.ts(1 hunks)src/hooks/use-user/use-user-update/index.ts(1 hunks)src/types/service/user.ts(2 hunks)
💤 Files with no reviewable changes (2)
- src/app/(user)/mypage/page.tsx
- src/hooks/use-user/index.ts
🧰 Additional context used
🧬 Code graph analysis (7)
src/hooks/use-user/use-user-notification/index.ts (3)
src/types/service/user.ts (1)
UpdateMyNotiParams(31-33)src/api/index.ts (1)
API(22-22)src/lib/query-key/query-key-user/index.ts (1)
userKeys(1-5)
src/components/ui/image-with-fallback/index.tsx (1)
src/components/ui/index.ts (1)
ImageWithFallback(3-3)
src/components/pages/user/profile/profile-edit-fields/image-field/index.tsx (2)
src/lib/utils.ts (1)
cn(74-76)src/components/icon/index.tsx (1)
Icon(55-61)
src/components/pages/user/profile/profile-edit-modal/index.tsx (3)
src/hooks/use-user/use-user-update/index.ts (1)
useUpdateUser(7-17)src/hooks/use-user/use-user-image-update/index.ts (1)
useUserImageUpdate(7-18)src/types/service/user.ts (1)
UpdateMePayload(21-25)
src/hooks/use-user/use-user-image-update/index.ts (3)
src/types/service/user.ts (1)
UpdateMyProfileImagePayload(27-29)src/api/index.ts (1)
API(22-22)src/lib/query-key/query-key-user/index.ts (1)
userKeys(1-5)
src/api/service/user-service/index.ts (2)
src/types/service/user.ts (9)
FollowParams(35-37)UpdateMePayload(21-25)User(1-15)UpdateMyProfileImagePayload(27-29)UpdateMyNotiParams(31-33)GetUserParams(17-19)GetNickNameAvailabilityParams(43-45)Availability(39-41)GetEmailAvailabilityParams(47-49)src/api/core/index.ts (1)
api(64-90)
src/components/pages/user/mypage/mypage-setting/index.tsx (2)
src/hooks/use-user/use-user-notification/index.ts (1)
useUpdateMyNotification(7-20)src/components/pages/user/mypage/mypage-setting-button/index.tsx (1)
MyPageToggleButton(12-19)
🔇 Additional comments (11)
next.config.ts (1)
25-30: S3 bucket allowlist addition looks correct.This should unblock
next/imageremote loading for the WeGo S3 host.src/components/pages/user/profile/profile-edit-fields/index.ts (1)
1-4: Clean barrel export; improves module boundaries.src/components/ui/index.ts (1)
1-5: UI export addition is fine (public surface updated).src/types/service/user.ts (1)
27-49: LGTM!The type definitions are well-structured. The rename to
UpdateMyProfileImagePayloadis more descriptive, and the new availability interfaces properly support the nickname/email validation features.src/components/pages/user/profile/profile-edit-modal/index.tsx (1)
82-84: LGTM!Good UX improvement with the combined pending state and dynamic button label feedback during submission.
src/components/pages/user/profile/profile-edit-fields/image-field/index.tsx (1)
24-51: LGTM!The render prop logic correctly handles the empty images case by ensuring a placeholder is always rendered. The
ImageWithFallbackcomponent will properly display the fallback image when theurlis an empty string.src/api/service/user-service/index.ts (5)
15-18: LGTM!The follow endpoint implementation is correct. POST method is appropriate for the follow action, and the path parameter will be properly URL-encoded by axios.
20-23: LGTM!The profile update endpoint correctly uses PATCH for partial updates and passes the payload as the request body.
25-30: LGTM!The profile image update endpoint correctly handles file uploads using FormData. The implementation follows the standard pattern for multipart/form-data file uploads.
39-42: LGTM!The user profile retrieval endpoint is correctly implemented with appropriate path parameter handling.
54-57: LGTM!The unfollow endpoint implementation is correct. DELETE method is appropriate for the unfollow action, and the path parameter will be properly URL-encoded by axios.
| // 4. 알림 설정 변경 | ||
| updatMyNotification: async (payload: UpdateMyNotiParams) => { | ||
| return api.patch<User>(`/users/notification/${payload.isNotificationEnabled}`); | ||
| updatMyNotification: async (params: UpdateMyNotiParams) => { | ||
| return api.patch<User>( | ||
| `/users/notification?isNotificationEnabled=${params.isNotificationEnabled}`, | ||
| ); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find and display the target file and relevant context
cat -n src/api/service/user-service/index.ts | head -50Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 1850
🏁 Script executed:
# Search for all usages of the function name (both correct and typo variants)
rg -n 'updatMyNotification|updateMyNotification' --type=ts --type=tsx -C2Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 98
🏁 Script executed:
# Search for all usages of updatMyNotification (the typo version)
rg -n 'updatMyNotification' -C2Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 1011
🏁 Script executed:
# Also search for updateMyNotification to see if it's used anywhere
rg -n 'updateMyNotification' -C2Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 53
🏁 Script executed:
# Check the api core to understand how axios is configured
cat -n src/api/core.ts 2>/dev/null || cat -n src/api/core/index.ts 2>/dev/null || find src/api -name "*core*" -type fRepository: WeGo-Together/WeGo_FrontEnd
Length of output: 3597
Fix function name typo and use proper query parameter handling.
Two issues identified:
- Function name has a typo: "updatMyNotification" should be "updateMyNotification"
- Query parameters should use axios config instead of string interpolation for consistency and best practices
This change affects at least one call site in src/hooks/use-user/use-user-notification/index.ts (line 10).
Apply this diff:
- // 4. 알림 설정 변경
- updatMyNotification: async (params: UpdateMyNotiParams) => {
- return api.patch<User>(
- `/users/notification?isNotificationEnabled=${params.isNotificationEnabled}`,
- );
+ // 4. 알림 설정 변경
+ updateMyNotification: async (params: UpdateMyNotiParams) => {
+ return api.patch<User>('/users/notification', null, {
+ params: { isNotificationEnabled: params.isNotificationEnabled },
+ });
},Update the call site in src/hooks/use-user/use-user-notification/index.ts (line 10) to use the corrected function name.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 4. 알림 설정 변경 | |
| updatMyNotification: async (payload: UpdateMyNotiParams) => { | |
| return api.patch<User>(`/users/notification/${payload.isNotificationEnabled}`); | |
| updatMyNotification: async (params: UpdateMyNotiParams) => { | |
| return api.patch<User>( | |
| `/users/notification?isNotificationEnabled=${params.isNotificationEnabled}`, | |
| ); | |
| }, | |
| // 4. 알림 설정 변경 | |
| updateMyNotification: async (params: UpdateMyNotiParams) => { | |
| return api.patch<User>('/users/notification', null, { | |
| params: { isNotificationEnabled: params.isNotificationEnabled }, | |
| }); | |
| }, |
🤖 Prompt for AI Agents
In src/api/service/user-service/index.ts around lines 32 to 37, rename the
function from "updatMyNotification" to "updateMyNotification" and change the
request to pass query parameters via axios config (use
api.patch<User>('/users/notification', undefined, { params: {
isNotificationEnabled: params.isNotificationEnabled } }) ) instead of string
interpolation; also update the call site in
src/hooks/use-user/use-user-notification/index.ts (around line 10) to call the
new updateMyNotification function name.
| // 6. 닉네임 중복 검사 | ||
| getNicknameAvailability: async (params: GetNickNameAvailabilityParams) => { | ||
| return api.get<Availability>(`/users/nickname/availability?nickname=${params.nickName}`); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the file structure and the endpoint in question
cat -n src/api/service/user-service/index.ts | head -60Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 2141
🏁 Script executed:
# Search for API endpoint definitions or tests that might show the correct parameter name
rg "nickname.*availability" -i -A 3 -B 3Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 1298
🏁 Script executed:
# Look for similar endpoints to understand the pattern used in the codebase
rg "api\.get.*params:" -A 2 -B 2Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 53
🏁 Script executed:
# Check if there's any API documentation or backend endpoint definition
find . -type f \( -name "*api*" -o -name "*spec*" -o -name "*swagger*" -o -name "*openapi*" \) | grep -E "\.(ts|js|md|json|yaml|yml)$" | head -20Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 53
🏁 Script executed:
# Look for tests related to this endpoint
rg "getNicknameAvailability" -A 5 -B 2Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 661
🏁 Script executed:
# Check the axios configuration to understand default behavior with params
rg "api\.get|axios.*get" -A 2 | head -40Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 1314
🏁 Script executed:
# Look for test files or actual usage of getNicknameAvailability
rg "getNicknameAvailability" -A 5 -B 2Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 661
🏁 Script executed:
# Check the axios/api core configuration
cat -n src/api/core/index.ts 2>/dev/null || find src/api/core -type f -name "*.ts" | head -5Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 3597
🏁 Script executed:
# Look for backend API documentation or OpenAPI spec
find . -type f \( -name "*.md" -o -name "*.json" -o -name "*.yaml" -o -name "*.yml" \) | xargs grep -l "nickname.*availability" 2>/dev/null | head -5Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 53
🏁 Script executed:
# Check if there's environment/config that might specify API endpoint details
find . -type f \( -name ".env*" -o -name "config*" -o -name "*api*config*" \) | head -10Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 84
🏁 Script executed:
# Look for any comments or documentation about the nickname endpoint
rg "nickname" -B 2 -A 2 | grep -E "(api|endpoint|param|query)" | head -20Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 709
🏁 Script executed:
# Look for any integration tests or E2E tests that use this endpoint
find . -type f -name "*.test.*" -o -name "*.spec.*" | xargs grep -l "getNicknameAvailability\|nickname.*availability" 2>/dev/nullRepository: WeGo-Together/WeGo_FrontEnd
Length of output: 55
🏁 Script executed:
# Check if there's a backend repository reference or API spec
cat README.md 2>/dev/null | grep -i "api\|backend\|backend-repo" | head -10Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 410
🏁 Script executed:
# Search for actual usage of these functions to see if they work with special characters
rg "getNicknameAvailability|getEmailAvailability" -A 3 -B 3 | head -30Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 909
Missing URL encoding for query parameters.
The nickname query parameter is not URL-encoded. Nicknames containing spaces or special characters (e.g., "John Doe", "user@123") will cause malformed URLs and request failures.
The api.get() function supports passing a config object as the second parameter, which allows axios to properly handle and encode query parameters. Apply this fix:
// 6. 닉네임 중복 검사
getNicknameAvailability: async (params: GetNickNameAvailabilityParams) => {
- return api.get<Availability>(`/users/nickname/availability?nickname=${params.nickName}`);
+ return api.get<Availability>('/users/nickname/availability', {
+ params: { nickname: params.nickName },
+ });
},Note: The same issue affects the getEmailAvailability endpoint (line 51) and updatMyNotification endpoint (line 35). Consider applying the same fix pattern to those endpoints as well.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 6. 닉네임 중복 검사 | |
| getNicknameAvailability: async (params: GetNickNameAvailabilityParams) => { | |
| return api.get<Availability>(`/users/nickname/availability?nickname=${params.nickName}`); | |
| }, | |
| // 6. 닉네임 중복 검사 | |
| getNicknameAvailability: async (params: GetNickNameAvailabilityParams) => { | |
| return api.get<Availability>('/users/nickname/availability', { | |
| params: { nickname: params.nickName }, | |
| }); | |
| }, |
🤖 Prompt for AI Agents
In src/api/service/user-service/index.ts around lines 44 to 47, the nickname
query parameter is concatenated into the URL without URL-encoding; change the
call to pass query parameters via the api.get second-argument config (use {
params: { nickname: params.nickName } }) so axios encodes them properly; apply
the same pattern to getEmailAvailability (around line 51) and
updatMyNotification (around line 35) to avoid malformed URLs for emails,
nicknames or other special-character values.
| onSubmit: async ({ value }) => { | ||
| console.log(value); | ||
| const { profileImage, nickName, profileMessage, mbti } = value; | ||
|
|
||
| // 프로필 항목 업데이트 조건 체크 | ||
| const nextProfileInfo: UpdateMePayload = { | ||
| ...(user.nickName !== value.nickName && { nickName }), | ||
| ...(user.profileMessage !== value.profileMessage && { profileMessage }), | ||
| ...(user.mbti !== value.mbti && { mbti }), | ||
| }; | ||
| if (Object.values(nextProfileInfo).length > 0) { | ||
| await updateUser(nextProfileInfo); | ||
| } | ||
|
|
||
| // 이미지 업데이트 조건 체크 | ||
| const imageFileObject = Object.values(profileImage)[0]; | ||
| if (imageFileObject) { | ||
| await updateUserImage({ file: imageFileObject }); | ||
| } | ||
| close(); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for failed mutations.
If either updateUser or updateUserImage fails, the modal closes without user feedback. Users won't know their changes weren't saved.
onSubmit: async ({ value }) => {
const { profileImage, nickName, profileMessage, mbti } = value;
// 프로필 항목 업데이트 조건 체크
const nextProfileInfo: UpdateMePayload = {
...(user.nickName !== value.nickName && { nickName }),
...(user.profileMessage !== value.profileMessage && { profileMessage }),
...(user.mbti !== value.mbti && { mbti }),
};
- if (Object.values(nextProfileInfo).length > 0) {
- await updateUser(nextProfileInfo);
- }
-
- // 이미지 업데이트 조건 체크
- const imageFileObject = Object.values(profileImage)[0];
- if (imageFileObject) {
- await updateUserImage({ file: imageFileObject });
+ try {
+ if (Object.keys(nextProfileInfo).length > 0) {
+ await updateUser(nextProfileInfo);
+ }
+
+ // 이미지 업데이트 조건 체크
+ const imageFileObject = Object.values(profileImage)[0];
+ if (imageFileObject) {
+ await updateUserImage({ file: imageFileObject });
+ }
+ close();
+ } catch (error) {
+ // Consider showing a toast notification here
+ console.error('프로필 업데이트 실패:', error);
}
- close();
},🤖 Prompt for AI Agents
In src/components/pages/user/profile/profile-edit-modal/index.tsx around lines
37 to 56, the submit handler currently awaits updateUser and updateUserImage but
closes the modal regardless of failures; wrap the mutation calls in a try/catch
so errors are caught, show a user-facing error (e.g., toast or set an error
state) when either updateUser or updateUserImage throws, only call close() when
all required updates succeed, and log the error for debugging; ensure both calls
remain awaited and preserve current behavior when there are no errors.
| export const ImageWithFallback = ({ | ||
| src, | ||
| fallbackSrc = 'https://plus.unsplash.com/premium_photo-1738592736106-a17b897c0ab1?q=80&w=1934&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', | ||
| ...rest | ||
| }: ImageWithFallbackProps) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a local fallback image instead of an external URL.
The default fallbackSrc points to an external Unsplash URL, which defeats the purpose of a reliable fallback. If Unsplash is unavailable, rate-limited, or the URL changes, the fallback itself will fail.
Consider using a local placeholder image:
export const ImageWithFallback = ({
src,
- fallbackSrc = 'https://plus.unsplash.com/premium_photo-1738592736106-a17b897c0ab1?q=80&w=1934&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
+ fallbackSrc = '/images/default-profile.png',
...rest
}: ImageWithFallbackProps) => {Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/components/ui/image-with-fallback/index.tsx around lines 12 to 16, the
default fallbackSrc is an external Unsplash URL which is unreliable; replace it
with a local static asset (e.g., import or reference a local placeholder in the
project assets/public folder) and update the component to use that local path as
the default fallbackSrc so the fallback always resolves even if external
services fail.
| export const useUpdateMyNotification = () => { | ||
| const queryClient = useQueryClient(); | ||
| const query = useMutation({ | ||
| mutationFn: (params: UpdateMyNotiParams) => API.userService.updatMyNotification(params), | ||
| onSuccess: (_data, _variables, _context) => { | ||
| queryClient.invalidateQueries({ queryKey: userKeys.all }); | ||
| console.log('요청 성공'); | ||
| }, | ||
| onError: () => { | ||
| console.log('요청 실패'); | ||
| }, | ||
| }); | ||
| return query; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for both method name variants
echo "=== Searching for updatMyNotification (possible typo) ==="
rg -n --type=ts -C2 '\bupdatMyNotification\b' src || echo "Not found"
echo ""
echo "=== Searching for updateMyNotification (correct spelling) ==="
rg -n --type=ts -C2 '\bupdateMyNotification\b' src || echo "Not found"
echo ""
echo "=== Locating API service definition ==="
fd --type=f -e ts -e tsx | xargs grep -l "userService" | head -10Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 1556
🏁 Script executed:
#!/bin/bash
# Check how common console.log is in hooks
echo "=== Console.log usage in hooks ==="
rg -n "console\.(log|error|warn)" src/hooks --type=ts | head -20
echo ""
echo "=== Console.log pattern in mutations/queries ==="
rg -n "console\.(log|error|warn)" src/api --type=ts | head -20Repository: WeGo-Together/WeGo_FrontEnd
Length of output: 1737
Consider fixing the typo updatMyNotification in the API service definition and remove console.log in favor of error handling.
The method name updatMyNotification (missing "e") is defined in src/api/service/user-service/index.ts:33 with the same typo, so the hook correctly calls the matching name—no runtime error here. However, the typo should be fixed at the source to follow naming conventions.
Console logging in mutation callbacks is common throughout the codebase, but it's noisy for production. Consider returning success/error state to the caller (via toast or UI feedback) instead. If removing console.log is a priority, apply the change consistently across other hooks using the same pattern.
🤖 Prompt for AI Agents
In src/hooks/use-user/use-user-notification/index.ts lines 7-20, fix the API
method typo and remove noisy console logs: rename the method in
src/api/service/user-service/index.ts from updatMyNotification to
updateMyNotification and update all call sites to
API.userService.updateMyNotification (including this hook), then remove
console.log calls in onSuccess/onError here and instead rely on the mutation's
returned state (or call a passed-in notification/toast handler) while keeping
queryClient.invalidateQueries in onSuccess and handling the error parameter in
onError for downstream handling.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
src/hooks/use-user/use-user-notification/index.ts (1)
7-21: console.log를 사용자 피드백 메커니즘으로 교체하는 것을 권장합니다.프로덕션 환경에서는
console.log대신 토스트 알림이나 mutation 상태를 통해 사용자에게 피드백을 제공하는 것이 좋습니다. 이 패턴은 다른 훅들에서도 동일하게 사용되고 있습니다.src/components/pages/user/profile/profile-edit-modal/index.tsx (2)
42-61: mutation 실패 시 에러 핸들링을 추가해야 합니다.현재
updateUser또는updateUserImage가 실패하더라도 모달이 닫힙니다. 사용자는 변경사항이 저장되지 않았다는 것을 알 수 없습니다.다음과 같이 try-catch를 추가하세요:
onSubmit: async ({ value }) => { const { profileImage, nickName, profileMessage, mbti } = value; // 프로필 항목 업데이트 조건 체크 const nextProfileInfo: UpdateMyInfoPayload = { ...(user.nickName !== value.nickName && { nickName }), ...(user.profileMessage !== value.profileMessage && { profileMessage }), ...(user.mbti !== value.mbti && { mbti }), }; - if (Object.keys(nextProfileInfo).length > 0) { - await updateUser(nextProfileInfo); - } - - // 이미지 업데이트 조건 체크 - const imageFileObject = Object.values(profileImage)[0]; - if (imageFileObject) { - await updateUserImage({ file: imageFileObject }); + try { + if (Object.keys(nextProfileInfo).length > 0) { + await updateUser(nextProfileInfo); + } + + // 이미지 업데이트 조건 체크 + const imageFileObject = Object.values(profileImage)[0]; + if (imageFileObject) { + await updateUserImage({ file: imageFileObject }); + } + close(); + } catch (error) { + console.error('프로필 업데이트 실패:', error); + // TODO: 토스트 알림 추가 } - close(); },
51-53: Object.values() 대신 Object.keys()를 사용해야 합니다.조건식에서
Object.values(nextProfileInfo).length를 사용하고 있는데, 이는 객체의 값 개수를 확인합니다. 객체가 비어있는지 확인하려면Object.keys(nextProfileInfo).length를 사용해야 합니다.다음과 같이 수정하세요:
- if (Object.values(nextProfileInfo).length > 0) { + if (Object.keys(nextProfileInfo).length > 0) { await updateUser(nextProfileInfo); }src/api/service/user-service/index.ts (1)
35-40: 쿼리 파라미터를 axios config를 통해 전달하세요.이전 리뷰에서 지적된 문자열 보간 문제가 여전히 남아있습니다. 다른 메서드들(getNicknameAvailability, getEmailAvailability, unfollowUser)과의 일관성을 위해 axios config 객체를 사용해야 합니다.
다음 diff를 적용하세요:
// 4. 알림 설정 변경 updateMyNotification: async (params: UpdateMyNotificationQueryParams) => { - return api.patch<User>( - `/users/notification?isNotificationEnabled=${params.isNotificationEnabled}`, - ); + return api.patch<User>('/users/notification', null, { + params: { isNotificationEnabled: params.isNotificationEnabled }, + }); },
🧹 Nitpick comments (4)
src/hooks/use-user/use-user-follow/index.ts (1)
7-20: 훅 패턴 개선이 잘 적용되었습니다. console.log를 제거하는 것을 권장합니다.파라미터를
mutationFn에서 받는 패턴으로 변경한 것은 적절합니다. 다만 프로덕션 환경에서는console.log대신 사용자 피드백(토스트 등)을 제공하거나 mutation의 상태를 반환하는 것이 좋습니다.src/hooks/use-user/use-user-update/index.ts (1)
14-14: 빈 에러 핸들러를 개선하는 것을 권장합니다.
onError가 빈 핸들러로 구현되어 있어 에러 발생 시 사용자에게 아무런 피드백이 제공되지 않습니다. 최소한 에러 로깅이나 사용자 알림을 추가하는 것이 좋습니다.다음과 같이 에러 핸들링을 추가할 수 있습니다:
- onError: () => {}, + onError: (error) => { + console.error('프로필 업데이트 실패:', error); + // TODO: 토스트 알림 추가 + },src/components/pages/user/profile/profile-edit-modal/index.tsx (1)
37-41: 순차 실행을 병렬 실행으로 개선하는 것을 권장합니다.TODO 코멘트에서 언급한 대로, 현재 순차적으로 실행되는 두 API 호출을
Promise.all을 사용하여 병렬로 실행하면 성능이 개선됩니다. 단, 두 API가 독립적이고 순서에 의존하지 않는 경우에만 적용하세요.다음과 같이 개선할 수 있습니다:
const promises: Promise<any>[] = []; if (Object.keys(nextProfileInfo).length > 0) { promises.push(updateUser(nextProfileInfo)); } const imageFileObject = Object.values(profileImage)[0]; if (imageFileObject) { promises.push(updateUserImage({ file: imageFileObject })); } if (promises.length > 0) { await Promise.all(promises); }src/hooks/use-user/use-user-unfollow/index.ts (1)
7-20: LGTM! follow 훅과 일관된 패턴이 적용되었습니다.언팔로우 훅이 팔로우 훅과 동일한 패턴으로 잘 구현되었습니다. 다만 다른 훅들과 마찬가지로
console.log를 사용자 피드백으로 교체하는 것을 권장합니다.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
src/api/service/user-service/index.ts(1 hunks)src/components/pages/user/profile/profile-edit-modal/index.tsx(3 hunks)src/components/pages/user/profile/profile-info/index.tsx(2 hunks)src/components/ui/image-with-fallback/index.tsx(1 hunks)src/hooks/use-user/use-user-follow/index.ts(1 hunks)src/hooks/use-user/use-user-get/index.ts(1 hunks)src/hooks/use-user/use-user-image-update/index.ts(1 hunks)src/hooks/use-user/use-user-notification/index.ts(1 hunks)src/hooks/use-user/use-user-unfollow/index.ts(1 hunks)src/hooks/use-user/use-user-update/index.ts(1 hunks)src/mock/service/user/user-mock.ts(3 hunks)src/types/service/user.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/hooks/use-user/use-user-image-update/index.ts
- src/components/ui/image-with-fallback/index.tsx
- src/hooks/use-user/use-user-get/index.ts
🧰 Additional context used
🧬 Code graph analysis (7)
src/hooks/use-user/use-user-notification/index.ts (3)
src/types/service/user.ts (1)
UpdateMyNotificationQueryParams(31-33)src/api/index.ts (1)
API(22-22)src/lib/query-key/query-key-user/index.ts (1)
userKeys(1-5)
src/hooks/use-user/use-user-unfollow/index.ts (1)
src/types/service/user.ts (1)
UnfollowQueryParams(39-41)
src/hooks/use-user/use-user-update/index.ts (4)
src/hooks/use-user/index.ts (1)
useUpdateUser(4-4)src/types/service/user.ts (1)
UpdateMyInfoPayload(21-25)src/api/index.ts (1)
API(22-22)src/lib/query-key/query-key-user/index.ts (1)
userKeys(1-5)
src/components/pages/user/profile/profile-edit-modal/index.tsx (3)
src/hooks/use-user/use-user-update/index.ts (1)
useUpdateUser(7-17)src/hooks/use-user/use-user-image-update/index.ts (1)
useUserImageUpdate(7-17)src/types/service/user.ts (1)
UpdateMyInfoPayload(21-25)
src/hooks/use-user/use-user-follow/index.ts (1)
src/types/service/user.ts (1)
FollowPathParams(35-37)
src/components/pages/user/profile/profile-info/index.tsx (2)
src/hooks/use-user/use-user-follow/index.ts (1)
useFollowUser(7-21)src/hooks/use-user/use-user-unfollow/index.ts (1)
useUnfollowUser(7-20)
src/api/service/user-service/index.ts (1)
src/types/service/user.ts (10)
FollowPathParams(35-37)UpdateMyInfoPayload(21-25)User(1-15)UpdateMyImagePayload(27-29)UpdateMyNotificationQueryParams(31-33)GetUserPathParams(17-19)GetNicknameAvailabilityQueryParams(47-49)Availability(43-45)GetEmailAvailabilityQueryParams(51-53)UnfollowQueryParams(39-41)
🪛 Biome (2.1.2)
src/components/pages/user/profile/profile-edit-modal/index.tsx
[error] 87-87: Avoid passing children using a prop
The canonical way to pass children in React is to use JSX elements
(lint/correctness/noChildrenProp)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: chromatic
🔇 Additional comments (12)
src/mock/service/user/user-mock.ts (1)
18-18: LGTM! 타입 정의 변경에 맞춰 필드명이 올바르게 업데이트되었습니다.
isFollowing에서isFollow로의 필드명 변경이 모든 mock 데이터에 일관되게 적용되었습니다.Also applies to: 34-34, 50-50
src/components/pages/user/profile/profile-info/index.tsx (2)
15-25: LGTM! 팔로우/언팔로우 로직이 명확하게 구현되었습니다.새로운 훅들을 사용하여 팔로우/언팔로우 기능이 깔끔하게 구현되었고, 각 액션에 적절한 핸들러가 연결되었습니다.
31-36: LGTM! 타입 변경에 맞춰 올바르게 업데이트되었습니다.
isFollowing에서isFollow로의 필드명 변경이 UI 렌더링 로직에 일관되게 적용되었습니다.src/types/service/user.ts (1)
14-14: LGTM! 타입 정의가 일관성 있고 명확하게 개선되었습니다.타입 이름들이 더 명확해졌고 (
PathParams,QueryParams등), 새로운 인터페이스들이 잘 정의되었습니다. 전체 코드베이스에서 일관되게 사용될 수 있는 좋은 타입 시스템입니다.Also applies to: 17-19, 21-25, 31-33, 35-53
src/api/service/user-service/index.ts (8)
1-13: 임포트 구성이 올바릅니다.새로 추가된 타입들이 메서드 시그니처와 일치하며 적절하게 구성되어 있습니다.
23-26: 메서드 구현이 올바릅니다.프로필 정보 업데이트를 위한 표준 REST 패턴이 적용되었습니다.
28-33: 파일 업로드 구현이 적절합니다.FormData를 올바르게 생성하고 파일을 첨부하여 이미지 업데이트를 처리하고 있습니다.
42-45: 사용자 조회 구현이 올바릅니다.경로 파라미터를 사용한 표준 GET 요청 패턴입니다.
47-52: 이전 리뷰 이슈가 해결되었습니다.닉네임 중복 검사 메서드가 이제 axios config를 통해 쿼리 파라미터를 전달하여 URL 인코딩이 올바르게 처리됩니다.
54-59: 이전 리뷰 이슈가 해결되었습니다.이메일 중복 검사 메서드가 이제 axios config를 통해 쿼리 파라미터를 전달하여 특수 문자(
+,@등)가 올바르게 인코딩됩니다.
61-66: 언팔로우 메서드 구현이 적절합니다.DELETE 요청에서 쿼리 파라미터를 axios config를 통해 전달하는 방식이 올바르게 구현되었습니다.
16-21: 백엔드 API 명세와 일치하는지 확인이 필요합니다.
followUser메서드가 POST 요청에서 쿼리 파라미터를 사용하고 있습니다. 일반적으로 POST 요청은 요청 바디에 데이터를 전달하지만, 여기서는 쿼리 파라미터(followNickname)를 사용하고 있습니다.unfollowUser(DELETE, 라인 62-65)도 동일한 패턴으로 쿼리 파라미터를 사용하고 있으므로 의도적인 API 설계로 보이지만, 백엔드 API 명세와 실제로 일치하는지 확인하세요.
wooktori
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다!
📝 변경 사항
💡 프로필/마이 페이지 업데이트 API 연결
💡 타입 네이밍 수정
1. Query Parameter:
*QueryParamsUnfollowQueryParams2. Path Parameter:
*PathParamsGetUserPathParams3. Body:
*PayloadsUpdateMyImagePayloads💡 Query Parameter 적용 방식 수정
Before
After
💡 ImageWithFallback 컴포넌트
💡 next.config.ts
💡 useUser 훅 수정
🔗 관련 이슈
Closes #48
🧪 테스트 방법
📸 스크린샷 (선택)
📋 체크리스트
💬 추가 코멘트
CodeRabbit Review는 자동으로 실행되지 않습니다.
Review를 실행하려면 comment에 아래와 같이 작성해주세요
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.