Conversation
Co-authored-by: CHAEEUN KIM <154000318+bongtta@users.noreply.github.com>
* fix: (QA/3) 홈으로 페이지 이동할 때 배너 이미지 로드 전에 텍스트만 뜨는 문제 해결 (#384) * fix: 홈으로 페이지 이동할 때 배너 이미지 로드 전에 텍스트만 뜨는 문제 해결 (#384) * fix: 배너 최소 높이 설정 (#384) * fix: 회원가입 design, gaEvent 수정 및 매칭 현황 내 중복 데이터 오류 해결 (#383) * fix: sign-up 페이지 배경색 짤리는 오류 수정 (#383) * fix: match-card-view gaEvent 삭제 (#383) * fix: gaEvent 위치 수정 (#383) * fix: 캐시가 섞이면서 데이터 중복되는 에러 해결 (#383) * fix: 주석 제거 (#383) --------- Co-authored-by: heesunee <heesun729@uos.ac.kr> --------- Co-authored-by: Yewon Kim <163109964+yeeeww@users.noreply.github.com> Co-authored-by: heesunee <heesun729@uos.ac.kr>
Co-authored-by: heesunee <heesun729@uos.ac.kr>
Walkthrough닉네임 중복 확인 로직을 useQuery 기반 재조회에서 useMutation 기반 명시적 호출로 전환했습니다. 회원가입과 프로필 수정 화면의 핸들러가 변경되었고, user-queries의 NICKNAME_CHECK가 제거되어 user-mutations에 추가되었습니다. 재시도 정책과 409(CONFLICT) 처리, 관련 상수 추가가 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant UI as Signup/EditProfile UI
participant MUT as useMutation(NICKNAME_CHECK)
participant API as GET /nickname/check
note over UI,MUT: 닉네임 중복확인 로직을 useQuery → useMutation으로 전환
U->>UI: 닉네임 중복확인 버튼 클릭
UI->>MUT: mutateAsync({ nickname })
MUT->>API: GET /nickname/check?nickname=...
alt 200 OK
API-->>MUT: success
MUT-->>UI: true (available)
UI-->>U: 사용 가능 상태 표시
else 409 CONFLICT
API-->>MUT: conflict
MUT-->>UI: false (duplicate)
UI-->>U: 중복 상태 표시
else Other Error
API-->>MUT: error
MUT-->>UI: throw
UI-->>U: 에러 처리/상태 초기화
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (5 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. 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 |
|
MATEBALL-STORYBOOK |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
src/pages/sign-up/constants/validation.ts (1)
44-44: 상수 위치/이름 정리: API 레이어가 pages 상수에 의존하지 않도록 분리 + 재시도 의미 명확화
- user-mutations.ts가 pages/sign-up/constants에 의존합니다. 공용 상수는 @constants(또는 @shared/constants)로 이동하는 게 적절합니다.
- 현재
failureCount < 2라면 실질 재시도는 1회입니다. 의도가 “최대 2회 재시도”라면 비교식을<=로, 아니면 상수명을...MAX_RETRIES = 1처럼 의미가 드러나게 바꾸는 걸 권장합니다.적용 예시(이 파일 기준 변경):
- export const NICKNAME_DUPLICATE_FAILURE_COUNT = 2; + // 공용 위치(@constants/retry.ts 등)로 이동 권장 + export const NICKNAME_CHECK_MAX_RETRIES = 1;user-mutations.ts에서는 아래처럼 수정:
- import { NICKNAME_DUPLICATE_FAILURE_COUNT } from '@pages/sign-up/constants/validation'; + import { NICKNAME_CHECK_MAX_RETRIES } from '@constants/retry'; - return failureCount < NICKNAME_DUPLICATE_FAILURE_COUNT; + return failureCount <= NICKNAME_CHECK_MAX_RETRIES;src/pages/edit-profile/edit-profile.tsx (1)
61-61: 중복 확인 중 다중 클릭 방지: pending 상태 노출
isPending을 구조분해해 버튼 비활성화/스피너 연결 권장.- const { mutateAsync: checkNickname } = useMutation(userMutations.NICKNAME_CHECK()); + const { + mutateAsync: checkNickname, + isPending: isCheckingNickname, + } = useMutation(userMutations.NICKNAME_CHECK());(변경은 선택 범위 밖이지만 참고) 버튼 비활성화 예:
<Button type="button" variant={ !!errors.nickname || nicknameVal.trim().length === 0 || isSubmitting || isCheckingNickname ? 'disabled' : 'skyblue' } disabled={!!errors.nickname || nicknameVal.trim().length === 0 || isSubmitting || isCheckingNickname} aria-busy={isCheckingNickname} label="중복 확인" onClick={handleCheckNickname} />src/pages/sign-up/components/signup-step.tsx (1)
55-55: 중복 확인 중 다중 클릭 방지: pending 상태 노출
isPending을 노출해 버튼 비활성화/로딩 상태에 사용하세요.- const { mutateAsync: checkNickname } = useMutation(userMutations.NICKNAME_CHECK()); + const { + mutateAsync: checkNickname, + isPending: isCheckingNickname, + } = useMutation(userMutations.NICKNAME_CHECK());(선택 범위 밖 참고) “중복 확인” 버튼:
<Button label="중복 확인" type="button" onClick={handleCheckNickname} disabled={!isNicknameValid || isCheckingNickname} aria-busy={isCheckingNickname} />src/shared/apis/user/user-mutations.ts (2)
75-77: mutationKey 추가로 디버깅/Devtools 가독성 개선뮤테이션 키를 명시하면 추적이 수월합니다.
- NICKNAME_CHECK: () => - mutationOptions<boolean, Error, { nickname: string }>({ + NICKNAME_CHECK: () => + mutationOptions<boolean, Error, { nickname: string }>({ + mutationKey: ['user', 'nickname-check'], mutationFn: async ({ nickname }) => {
88-93: 재시도 정책 의도 확인 필요(비교식/상수명 불일치 가능성)현재 로직은 “실패 1회까지 재시도 허용”입니다. 의도가 “최대 2회 재시도”라면
<=로 변경하거나, 의도가 1회 재시도라면 상수를...MAX_RETRIES = 1로 명확히 하세요. 또한 상수는 @constants 쪽으로 이동 권장(모듈 의존 방향).- return failureCount < NICKNAME_DUPLICATE_FAILURE_COUNT; + // 예: 1회 재시도 의도라면 MAX_RETRIES = 1로 네이밍 변경 권장 + return failureCount <= NICKNAME_CHECK_MAX_RETRIES;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/pages/edit-profile/edit-profile.tsx(2 hunks)src/pages/sign-up/components/signup-step.tsx(3 hunks)src/pages/sign-up/constants/validation.ts(1 hunks)src/shared/apis/user/user-mutations.ts(2 hunks)src/shared/apis/user/user-queries.ts(0 hunks)
💤 Files with no reviewable changes (1)
- src/shared/apis/user/user-queries.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: heesunee
PR: MATEBALL/MATEBALL-CLIENT#95
File: src/pages/sign-up/components/nickname-step.tsx:28-30
Timestamp: 2025-07-09T18:07:41.693Z
Learning: heesunee는 src/pages/sign-up/components/nickname-step.tsx의 onSubmit 핸들러 API 호출을 쿼리와 함께 통합해서 처리할 예정이므로, 이 부분에 대해 다시 언급하지 않아야 합니다.
Learnt from: heesunee
PR: MATEBALL/MATEBALL-CLIENT#78
File: src/pages/sign-up/utils/age-calculate.ts:1-4
Timestamp: 2025-07-09T09:08:46.371Z
Learning: heesunee는 나이 계산 시 한국식 나이 시스템을 사용하므로, 정확한 생일 기반 계산보다는 출생연도 기준의 간단한 계산 방식을 선호합니다.
📚 Learning: 2025-07-09T18:07:41.693Z
Learnt from: heesunee
PR: MATEBALL/MATEBALL-CLIENT#95
File: src/pages/sign-up/components/nickname-step.tsx:28-30
Timestamp: 2025-07-09T18:07:41.693Z
Learning: heesunee는 src/pages/sign-up/components/nickname-step.tsx의 onSubmit 핸들러 API 호출을 쿼리와 함께 통합해서 처리할 예정이므로, 이 부분에 대해 다시 언급하지 않아야 합니다.
Applied to files:
src/pages/edit-profile/edit-profile.tsxsrc/pages/sign-up/components/signup-step.tsx
📚 Learning: 2025-07-09T17:24:19.755Z
Learnt from: heesunee
PR: MATEBALL/MATEBALL-CLIENT#94
File: src/pages/sign-up/sign-up.tsx:4-4
Timestamp: 2025-07-09T17:24:19.755Z
Learning: heesunee는 src/pages/sign-up/sign-up.tsx의 isNicknameStep 하드코딩된 값을 퍼널 패턴으로 카카오 통합과 함께 나중에 처리할 예정이므로, 이 부분에 대해 다시 언급하지 않아야 합니다.
Applied to files:
src/pages/sign-up/components/signup-step.tsx
🧬 Code graph analysis (3)
src/pages/edit-profile/edit-profile.tsx (1)
src/shared/apis/user/user-mutations.ts (1)
userMutations(20-95)
src/shared/apis/user/user-mutations.ts (4)
src/shared/apis/base/http.ts (1)
get(3-5)src/shared/constants/api.ts (1)
END_POINT(3-52)src/shared/constants/response.ts (1)
HTTP_STATUS(1-11)src/pages/sign-up/constants/validation.ts (1)
NICKNAME_DUPLICATE_FAILURE_COUNT(44-44)
src/pages/sign-up/components/signup-step.tsx (1)
src/shared/apis/user/user-mutations.ts (1)
userMutations(20-95)
🔇 Additional comments (1)
src/pages/sign-up/components/signup-step.tsx (1)
23-23: LGTM: useMutation 도입쿼리→뮤테이션 전환 방향성 적절합니다.
| const available = await checkNickname({ nickname: nicknameVal }); | ||
| setNicknameStatus(available ? 'available' : 'duplicate'); | ||
| } catch { |
There was a problem hiding this comment.
경합(race) 조건: 응답 도착 시 닉네임이 바뀐 경우 오표시 가능
요청 시점의 닉네임과 응답 시점의 현재 값이 다르면 상태 업데이트를 스킵해야 합니다. 또한 공백은 트림해서 전송을 권장합니다.
- const available = await checkNickname({ nickname: nicknameVal });
- setNicknameStatus(available ? 'available' : 'duplicate');
+ const requested = nicknameVal.trim();
+ const available = await checkNickname({ nickname: requested });
+ // 사용자가 입력을 바꿨다면 이전 응답은 무시
+ if (requested !== watch('nickname').trim()) return;
+ setNicknameStatus(available ? 'available' : 'duplicate');📝 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.
| const available = await checkNickname({ nickname: nicknameVal }); | |
| setNicknameStatus(available ? 'available' : 'duplicate'); | |
| } catch { | |
| const requested = nicknameVal.trim(); | |
| const available = await checkNickname({ nickname: requested }); | |
| // 사용자가 입력을 바꿨다면 이전 응답은 무시 | |
| if (requested !== watch('nickname').trim()) return; | |
| setNicknameStatus(available ? 'available' : 'duplicate'); | |
| } catch { |
🤖 Prompt for AI Agents
In src/pages/edit-profile/edit-profile.tsx around lines 115 to 117, the nickname
availability check can race: if the user changes the nickname before the async
response arrives the component may set the wrong status. Trim the nickname
before sending (const trimmed = nicknameVal.trim()) and call checkNickname({
nickname: trimmed }); after the await, compare the latest current nickname state
(or input value) with the trimmed value used for the request and only call
setNicknameStatus(available ? 'available' : 'duplicate') if they match; also
ensure the catch block does not swallow this logic (only handle/report the
error) and does not update state when the names differ.
| const available = await checkNickname({ nickname: nicknameValue }); | ||
| setNicknameStatus(available ? 'available' : 'duplicate'); | ||
| } catch { |
There was a problem hiding this comment.
경합(race) 조건: 응답/입력 불일치 시 잘못된 상태 표시 가능
요청 당시 닉네임을 캡처해 비교하세요. 공백 트림도 권장.
- const available = await checkNickname({ nickname: nicknameValue });
- setNicknameStatus(available ? 'available' : 'duplicate');
+ const requested = nicknameValue.trim();
+ const available = await checkNickname({ nickname: requested });
+ if (requested !== (watch('nickname') ?? '').trim()) return;
+ setNicknameStatus(available ? 'available' : 'duplicate');📝 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.
| const available = await checkNickname({ nickname: nicknameValue }); | |
| setNicknameStatus(available ? 'available' : 'duplicate'); | |
| } catch { | |
| const requested = nicknameValue.trim(); | |
| const available = await checkNickname({ nickname: requested }); | |
| if (requested !== (watch('nickname') ?? '').trim()) return; | |
| setNicknameStatus(available ? 'available' : 'duplicate'); | |
| } catch { |
🤖 Prompt for AI Agents
In src/pages/sign-up/components/signup-step.tsx around lines 105 to 107, the
nickname availability result may be applied to stale input if the user changed
the field before the async check returned; capture and trim the current nickname
into a local variable before calling checkNickname, then after awaiting the
response verify that the field's current trimmed value still matches that
captured value before calling setNicknameStatus(available ? 'available' :
'duplicate'); this ensures you only update status for the nickname that was
actually checked.
#️⃣ Related Issue
Closes #393
☀️ New-insight
💎 PR Point
닉네임 중복확인의 경우
사용자가 입력 값을 바꾸고 버튼을 눌러야만 요청이 발생 -> 자동호출 필요없음
매번 다른 값으로 확인하므로 캐싱도 큰 의미 없음, 따라서 useMutation으로 수정하는게 맞다고 판단했습니다.
const available = await checkNickname({ nickname: nicknameValue });처럼 결과를 직관적으로 받을 수 있어요.Summary by CodeRabbit