Conversation
Walkthrough닉네임 중복 확인 로직을 도입하고, 가입/프로필수정 화면에 확인 버튼과 상태 흐름을 추가했습니다. 이를 위해 사용자 쿼리, 엔드포인트, 쿼리 키를 신설했으며, 관련 상수/타입을 확장했습니다. 부가적으로 VSCode 설정 정리, TS lib 타겟(ES2022) 상향, hasOwn 사용 교체가 포함되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as 사용자
participant V as 가입/프로필 화면(UI)
participant RQ as react-query
participant API as /v2/users/info?nickname=...
participant S as 서버
U->>V: 닉네임 입력
V->>V: nicknameStatus = idle
U->>V: "중복 확인" 클릭
V->>V: nicknameStatus = checking
V->>RQ: refetch(NICKNAME_CHECK(nickname))
RQ->>API: GET
API->>S: 요청 전달
alt 닉네임 사용 가능 (200)
S-->>API: 200 OK
API-->>RQ: true
RQ-->>V: true
V->>V: nicknameStatus = available
else 닉네임 중복 (409)
S-->>API: 409 CONFLICT
API-->>RQ: false
RQ-->>V: false
V->>V: nicknameStatus = duplicate
else 기타 오류
S-->>API: 5xx/네트워크 오류
API-->>RQ: throw
RQ-->>V: error
V->>V: nicknameStatus = idle
end
U->>V: 제출 버튼 클릭
V->>V: nicknameStatus === 'available' 인 경우만 진행
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
MATEBALL-STORYBOOK |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (12)
src/pages/sign-up/constants/NOTICE.ts (1)
19-19: 메시지 마침표 일관성 맞추기동일한 톤 유지 차원에서 성공 메시지(
NICKNAME_SUCCESS_MESSAGE)처럼 마침표를 붙이는 것이 좋아 보입니다.-export const NICKNAME_DUPLICATED = '이미 존재하는 닉네임이에요'; +export const NICKNAME_DUPLICATED = '이미 존재하는 닉네임이에요.';src/pages/sign-up/types/nickname-types.ts (1)
5-5: 네트워크 실패 표현을 위한 상태 추가 고려API 오류를 구분할 수 있도록
'error'상태를 추가하면 UI 분기(재시도, 토스트 등)에 유용합니다.-export type NicknameStatus = 'idle' | 'checking' | 'available' | 'duplicate'; +export type NicknameStatus = 'idle' | 'checking' | 'available' | 'duplicate' | 'error';tsconfig.app.json (1)
6-6: Object.hasOwn 사용 위치(src/pages/match/utils/match-status.ts:45) 런타임 호환성 검토
- 옵션 A: 브라우저 지원이 충분하다면
target을 ES2022로 상향- 옵션 B: 공용 유틸에 폴리필/헬퍼 적용
export const hasOwn = (obj: object, key: PropertyKey) => // @ts-ignore Object.hasOwn ? Object.hasOwn(obj, key) : Object.prototype.hasOwnProperty.call(obj, key);src/shared/constants/api.ts (1)
15-16: 엔드포인트 의미 명확성 및 인코딩 처리
encodeURIComponent사용은 적절합니다. 다만 동일 경로(/v2/users/info)가USER_INFO와 중복되어 있어, 서버에서nickname쿼리 유무에 따라 서로 다른 의미를 제공한다는 전제가 필요합니다. 백엔드 계약을 한 번만 확인해 주세요(상태 코드/응답 스키마 포함).가독성 측면의 대안(선호 시):
- `/v2/users/info?nickname=${encodeURIComponent(nickname)}`, + `/v2/users/info?${new URLSearchParams({ nickname }).toString()}`,src/shared/constants/query-key.ts (1)
8-8: 키 정규화 여부 확인(대소문자/공백 민감도)API가 닉네임을 대소문자 구분 없이 처리하거나 공백을 금지한다면, 캐시 키에
trim()(및 필요 시toLowerCase())을 적용해 중복 캐시를 줄일 수 있습니다. 서버 규칙에 따라 결정 부탁드립니다.- NICKNAME_CHECK: (nickname: string) => [...USER_KEY.ALL, 'nickname-check', nickname] as const, + NICKNAME_CHECK: (nickname: string) => [...USER_KEY.ALL, 'nickname-check', nickname.trim()] as const,.vscode/settings.json (1)
2-26: Biome 고정 설정 👍, JSON/JSONC 포맷터 범위 보완 제안팀 일관성 확보에는 좋아 보입니다. 추가로 일반 JSON도 Biome/빌트인 포맷터로 명시하면 혼선을 줄일 수 있습니다.
아래처럼 JSON도 명시 추가 제안:
"[javascriptreact]": { "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true }, + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, "[jsonc]": { "editor.defaultFormatter": "vscode.json-language-features" }또한
.vscode/extensions.json에 Biome 추천 확장을 추가하면 새 참가자 환경 일관성이 좋아집니다:{ "recommendations": ["biomejs.biome"] }src/shared/apis/user/user-queries.ts (1)
30-50: 닉네임 트림/취소 가능 신호 적용으로 레이스·메모리 개선 제안
- 입력 공백 트림을 쿼리 레이어에서 강제해 UI별 상이한 처리로 인한 오탐을 방지.
- React Query가 주는
signal을 axios에 전달해 탭 이탈/재요청 시 안전하게 취소.- 타자마다 새 키가 생기는 캐시를 짧게 유지(
gcTime)해 메모리 발자국 축소.- NICKNAME_CHECK: (nickname: string) => - queryOptions<boolean>({ - queryKey: USER_KEY.NICKNAME_CHECK(nickname), - enabled: false, - queryFn: async () => { + NICKNAME_CHECK: (nickname: string) => { + const nick = nickname.trim(); + return queryOptions<boolean>({ + queryKey: USER_KEY.NICKNAME_CHECK(nick), + enabled: false, + gcTime: 30_000, + queryFn: async ({ signal }) => { try { - await get<void>(END_POINT.GET_NICKNAME_CHECK(nickname)); + await get<void>(END_POINT.GET_NICKNAME_CHECK(nick), { signal }); return true; } catch (e) { if (axios.isAxiosError(e) && e.response?.status === HTTP_STATUS.CONFLICT) { return false; } throw e; } }, - retry: (failureCount, error) => { - if (axios.isAxiosError(error) && error.response?.status === HTTP_STATUS.CONFLICT) - return false; - return failureCount < 2; - }, - }), + retry: 2, + }); + },src/pages/sign-up/components/signup-step.tsx (3)
92-101: 중복확인 시 트림/레이스 가드 및 버튼 중복클릭 방지
- 요청 전
trim()으로 서버 체크 값 일치.- 응답 도착 시 현재 입력값이 바뀌었으면 상태 갱신 스킵해 플리커 방지.
- 체크 중엔 버튼 비활성화.
const handleCheckNickname = async () => { - if (!isNicknameValid) return; + if (!isNicknameValid) return; + const current = nicknameValue.trim(); setNicknameStatus('checking'); try { const { data } = await refetchNicknameCheck(); - setNicknameStatus(data ? 'available' : 'duplicate'); + // 입력값이 바뀌었으면 결과 반영하지 않음 + if (watch('nickname').trim() !== current) return; + setNicknameStatus(data ? 'available' : 'duplicate'); } catch { setNicknameStatus('idle'); } }; ... <Button label="중복 확인" type="button" className="cap_14_sb ml-auto w-fit px-[1.6rem] py-[0.6rem]" onClick={handleCheckNickname} - disabled={!isNicknameValid} + disabled={!isNicknameValid || nicknameStatus === 'checking'} />Also applies to: 134-140
121-130: 스키마 에러 메시지 연동으로 UX 개선현재
duplicate/available만 표시합니다. 스키마 에러(errors.nickname)도 노출되도록 합치면 사용자가 더 빠르게 수정할 수 있습니다. 에러일 땐isError도 true로.- validationMessage={ - nicknameStatus === 'duplicate' - ? NICKNAME_DUPLICATED - : nicknameStatus === 'available' - ? NICKNAME_SUCCESS_MESSAGE - : undefined - } - isError={nicknameStatus === 'duplicate'} + validationMessage={ + nicknameStatus === 'duplicate' + ? NICKNAME_DUPLICATED + : errors.nickname?.message ?? (nicknameStatus === 'available' ? NICKNAME_SUCCESS_MESSAGE : undefined) + } + isError={!!errors.nickname || nicknameStatus === 'duplicate'} isValid={nicknameStatus === 'available'}
53-55: 타이핑마다 새로운 쿼리 키 생성에 따른 캐시 증가 고려
enabled: false라도 닉네임이 바뀔 때마다 각 키가 5분 동안 남습니다. 상단 제안처럼gcTime을 짧게 두면 완화됩니다.src/pages/edit-profile/edit-profile.tsx (2)
112-121: 중복확인 트림/레이스 가드 및 로딩 중 버튼 비활성화사인업 쪽과 동일한 개선을 권장합니다.
const handleCheckNickname = async () => { - if (errors.nickname || nicknameVal.trim().length < 2) return; + if (errors.nickname || nicknameVal.trim().length < 2) return; + const current = nicknameVal.trim(); setNicknameStatus('checking'); try { const { data } = await refetchNicknameCheck(); - setNicknameStatus(data ? 'available' : 'duplicate'); + if (watch('nickname').trim() !== current) return; + setNicknameStatus(data ? 'available' : 'duplicate'); } catch { setNicknameStatus('idle'); } }; ... <Button type="button" variant="skyblue" label="중복 확인" onClick={handleCheckNickname} - disabled={!!errors.nickname || nicknameVal.trim().length === 0 || isSubmitting} + disabled={!!errors.nickname || nicknameVal.trim().length === 0 || isSubmitting || nicknameStatus === 'checking'} className="cap_14_sb mt-[0.8rem] w-auto px-[1.6rem] py-[0.6rem] disabled:text-white" />Also applies to: 151-158
138-146: 닉네임 입력의 스키마 에러 메시지도 표시중복만이 아니라 유효성 에러도 함께 보여주면 수정이 빠릅니다.
- validationMessage={ - nicknameStatus === 'duplicate' - ? NICKNAME_DUPLICATED - : nicknameStatus === 'available' - ? NICKNAME_SUCCESS_MESSAGE - : undefined - } - isError={nicknameStatus === 'duplicate'} + validationMessage={ + nicknameStatus === 'duplicate' + ? NICKNAME_DUPLICATED + : (errors.nickname?.message ?? (nicknameStatus === 'available' ? NICKNAME_SUCCESS_MESSAGE : undefined)) + } + isError={!!errors.nickname || nicknameStatus === 'duplicate'} isValid={nicknameStatus === 'available'}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
.vscode/settings.json(1 hunks)src/pages/edit-profile/edit-profile.tsx(5 hunks)src/pages/match/utils/match-status.ts(1 hunks)src/pages/sign-up/components/signup-step.tsx(6 hunks)src/pages/sign-up/constants/NOTICE.ts(1 hunks)src/pages/sign-up/types/nickname-types.ts(1 hunks)src/shared/apis/user/user-queries.ts(2 hunks)src/shared/constants/api.ts(1 hunks)src/shared/constants/query-key.ts(1 hunks)tsconfig.app.json(1 hunks)
🧰 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#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 하드코딩된 값을 퍼널 패턴으로 카카오 통합과 함께 나중에 처리할 예정이므로, 이 부분에 대해 다시 언급하지 않아야 합니다.
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-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/constants/NOTICE.tssrc/pages/edit-profile/edit-profile.tsxsrc/pages/sign-up/components/signup-step.tsx
📚 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
🧬 Code graph analysis (4)
src/pages/match/utils/match-status.ts (1)
src/shared/components/chip/styles/chip-variants.ts (1)
chipVariantOptions(3-36)
src/shared/apis/user/user-queries.ts (4)
src/shared/constants/query-key.ts (1)
USER_KEY(2-13)src/shared/apis/base/http.ts (1)
get(3-5)src/shared/constants/api.ts (1)
END_POINT(3-46)src/shared/constants/response.ts (1)
HTTP_STATUS(1-11)
src/pages/edit-profile/edit-profile.tsx (4)
src/pages/sign-up/types/nickname-types.ts (1)
NicknameStatus(5-5)src/shared/apis/user/user-queries.ts (1)
userQueries(9-51)src/pages/sign-up/constants/validation.ts (1)
NICKNAME_PLACEHOLDER(21-21)src/pages/sign-up/constants/NOTICE.ts (3)
NICKNAME_RULE_MESSAGE(9-9)NICKNAME_DUPLICATED(19-19)NICKNAME_SUCCESS_MESSAGE(11-11)
src/pages/sign-up/components/signup-step.tsx (3)
src/pages/sign-up/types/nickname-types.ts (1)
NicknameStatus(5-5)src/shared/apis/user/user-queries.ts (1)
userQueries(9-51)src/pages/sign-up/constants/NOTICE.ts (3)
NICKNAME_RULE_MESSAGE(9-9)NICKNAME_DUPLICATED(19-19)NICKNAME_SUCCESS_MESSAGE(11-11)
⏰ 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 (2)
src/pages/match/utils/match-status.ts (1)
45-45: Object.hasOwn 사용은 OK, 런타임 지원만 확인 부탁ES2022 이상의 런타임(또는 폴리필)이 보장되는지 확인해 주세요. 구형 브라우저 타깃이 섞여 있다면 폴리필이나 기존
hasOwnProperty.call로의 폴백이 필요할 수 있습니다.src/shared/apis/user/user-queries.ts (1)
45-49: retry 횟수 의도 확인 필요
failureCount < 2는 최대 1회 재시도입니다. “최대 2회 재시도” 의도라면retry: 2또는failureCount < 3로 바꾸세요.
#️⃣ Related Issue
Closes #327
☀️ New-insight
💎 PR Point
📸 Screenshot
Summary by CodeRabbit