Skip to content

feat: SP1 회원가입, 약관동의 api 연결#324

Merged
heesunee merged 24 commits intodevelopfrom
feat/#323/sp1-signup-api
Aug 31, 2025
Merged

feat: SP1 회원가입, 약관동의 api 연결#324
heesunee merged 24 commits intodevelopfrom
feat/#323/sp1-signup-api

Conversation

@heesunee
Copy link
Contributor

@heesunee heesunee commented Aug 29, 2025

#️⃣ Related Issue

Closes #323

☀️ New-insight

  • 이전 mutate가 성공한 후, 그 다음 mutate를 전송하기 위해 mutateSync를 사용한다!

💎 PR Point

  • 약관 동의, 개인정보 제공 동의 > svg 클릭시 해당 notion으로 이동하게 연결했습니다
  • 기존 nickname/user로 나뉘어있던 api를 sp1에 해당하는 user_info api 하나로 수정했습니다.
  • 이용약관 동의 api 추가했습니다.
const onSubmit = async (data: UserInfoFormValues) => {
  const userData: postUserInfoRequest = {
    nickname: data.nickname,
    introduction: data.introduction,
    birthYear: Number(data.birthYear),
    gender: data.gender,
  };

  try {
    await agreementInfoMutaion.mutateAsync({ hasAccepted: true });
    await userInfoMutation.mutateAsync(userData);
  } catch (e) {
    console.error('signup failed:', e);
  }
};
  • 사용자가 입력한 nickname, introduction등의 정보를 서버에서 요구하는 postUserInfoRequest 형태로 변환합니다.
  • 약관 동의 정보는 agreementInfoMutation.mutateAsync({ hasAccepted: true }) 호출을 통해
    사용자가 서비스 약관에 동의했음을 서버에 먼저 기록합니다.
  • 약관 동의가 성공하면 이어서 userInfoMutation.mutateAsync(userData)를 호출합니다.
  • 두 요청 중 하나라도 실패하면 catch 블록에서 에러를 콘솔에 출력합니다. 실패시 가입 프로세스가 중단되며, 이후 후속 로직은 실행되지 않습니다.
  • mutateAsync를 이용해 순차 실행을 보장하는 구조로 작성했습니다.

📸 Screenshot

테스트는 서버에서 제 유저 정보 DB 날려주면 해보고 추가할게용

Summary by CodeRabbit

  • 신규 기능

    • 회원가입에 동의 단계와 약관/개인정보 링크가 추가되었고 새 탭으로 열립니다.
    • 편집 페이지(프로필 수정)와 선택 그룹 UI가 추가되었습니다.
    • 로그아웃 기능 및 프로필 화면에서 문의/의견 외부 링크가 추가되었습니다.
    • 공통 Divider 컴포넌트가 도입되었습니다.
  • 개선 사항

    • 가입 폼이 자기소개(introduction)를 포함한 단일 제출로 통합되어 흐름이 간소화되었습니다.
    • 버튼에 disabled 스타일 변형이 추가되었습니다.
  • 기타

    • 푸터의 매칭 플랫폼 고지 문구가 제거되었습니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 29, 2025

Warning

Rate limit exceeded

@heesunee has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 43 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between bf7bb50 and 8f7b650.

📒 Files selected for processing (3)
  • src/pages/edit-profile/edit-profile.tsx (1 hunks)
  • src/pages/sign-up/components/signup-step.tsx (4 hunks)
  • src/pages/sign-up/constants/NOTICE.ts (1 hunks)

Walkthrough

회원가입 흐름을 v2 API로 재구성. 약관 동의(AGREEMENT_INFO) 요청을 먼저 호출한 뒤 사용자 정보(USER_INFO: nickname, introduction, birthYear, gender)를 단일로 전송하도록 변경. CheckboxRow에 외부 link prop 추가 및 agreement-step에 TERMS/PRIVACY 링크 주입. 스키마·상수명 information → introduction으로 변경.

Changes

Cohort / File(s) Summary
Sign-up UI 컴포넌트
src/pages/sign-up/components/agreement-step.tsx, src/pages/sign-up/components/checkbox-row.tsx, src/pages/sign-up/components/signup-step.tsx
CheckboxRow에 선택적 link?: string prop 추가 및 우측 앵커 렌더링으로 outer element를 div로 변경. agreement-stepTERMS_LINK/PRIVACY_LINK 주입. signup-step은 필드명 informationintroduction으로 변경하고 AGREEMENT_INFO → USER_INFO 순으로 mutateAsync 호출하도록 제출 흐름 재구성(네비게이션/쿼리 무효화 위치 조정, try/catch 추가).
검증 상수·스키마
src/pages/sign-up/constants/validation.ts, src/pages/sign-up/schema/validation-schema.ts
INFORMATION_* 식별자를 INTRODUCTION_*으로 리네이밍. 스키마 타입명 NicknameSchema/NicknameFormValuesUserInfoSchema/UserInfoFormValues, 스키마 필드 informationintroduction 반영.
링크 상수
src/pages/sign-up/constants/LINK.ts
TERMS_LINK, PRIVACY_LINK 상수 추가(외부 Notion URL).
API/뮤테이션·엔드포인트·쿼리 키·타입
src/shared/apis/user/user-mutations.ts, src/shared/constants/api.ts, src/shared/constants/query-key.ts, src/shared/types/user-types.ts
v2 엔드포인트로 전환: USER_INFO/v2/users/info, AGREEMENT_INFO 추가(/v2/users/consent), 닉네임 전용 엔드포인트/뮤테이션 제거. userMutationsAGREEMENT_INFO, LOGOUT 추가; USER_INFO{ nickname, introduction, birthYear, gender } 전송 및 onSuccess/onError 핸들러 추가. 타입 postUserInfoRequest 확장 및 postAgreementInfoRequest 도입. USER_KEYAGREEMENT, LOGOUT 추가.
프로필/편집 관련 추가
src/pages/edit-profile/*, src/shared/components/divider/divider.tsx, src/pages/profile/*
새 EditProfile 페이지 및 SelectionGroup 컴포넌트, mockEditData 추가. Profile 페이지에 로그아웃, 요청/피드백 외부 링크 및 편집 네비게이션 추가. Divider 컴포넌트 추가.
스타일/컴포넌트 변경
src/shared/components/button/.../button-variants.ts, src/shared/components/card/.../card-variants.ts, src/shared/components/footer/*, src/shared/components/header/*
버튼 variant에 disabled 추가. 카드 active variant에서 outline 두께 제거(클래스 변경). Footer에서 MATCHING_PLATFORM_NOTICE 제거 및 간격 조정. Header가 PROFILE_EDIT 경로에 대해 배경 처리 확장.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User as 사용자
  participant AgreementStep as AgreementStep
  participant CheckboxRow as CheckboxRow
  participant SignupStep as SignupStep
  participant Mutations as userMutations
  participant API as Backend

  Note over User,AgreementStep: 약관 동의 단계
  User->>CheckboxRow: 약관 체크 및 링크 확인
  User->>AgreementStep: 약관 동의 제출
  AgreementStep->>Mutations: AGREEEMENT_INFO.mutateAsync({ hasAccepted: true })
  Mutations->>API: POST /v2/users/consent
  API-->>Mutations: 200 OK
  Mutations-->>AgreementStep: success

  Note over User,SignupStep: 사용자 정보 제출 단계
  User->>SignupStep: 닉네임·소개·출생연도·성별 입력 후 제출
  SignupStep->>Mutations: USER_INFO.mutateAsync({ nickname, introduction, birthYear, gender })
  Mutations->>API: POST /v2/users/info
  API-->>Mutations: 200 OK
  Mutations-->>SignupStep: success (invalidate USER_KEY.ALL & router.navigate HOME)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
회원가입 페이지 API 연결: 약관 동의 API 연동 (#323)
회원 정보 제출 API를 v2로 전환 및 필드 확장(nickname, introduction) (#323)
프론트 폼/스키마를 information→introduction으로 일치화 (#323)
UI에서 약관/개인정보 링크 제공 (#323)

Possibly related PRs

Suggested reviewers

  • Dubabbi
  • bongtta
  • yeeeww

Poem

"깡총깡총, 체크를 찍고 링크를 톡,
약관 읽고 한숨 쉬어도 괜찮아, 넌 괜찮아.
소개 한 줄, 닉네임 한 번,
동의 먼저, 정보는 그다음—
토끼가 응원해요, v2로 훨훨!"

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#323/sp1-signup-api

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

MATEBALL-STORYBOOK
⚾ Storybook 배포가 완료되었습니다!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/pages/sign-up/components/checkbox-row.tsx (1)

15-29: 체크 영역 접근성/터치 타겟 확대

아이콘만 버튼으로 클릭 가능하고 라벨은 비활성입니다. 버튼에 라벨까지 포함하고 WAI-ARIA(role="checkbox", aria-checked)를 부여해 키보드/스크린리더 접근성을 개선하세요.

-  <div className="flex items-center gap-[0.8rem]">
-    <button type="button" className="cursor-pointer" onClick={onClick}>
-      <Icon name="check-filled" className={checked ? 'text-main-800' : 'text-gray-300'} />
-    </button>
-    <span className="body_16_m">{label}</span>
-  </div>
+  <button
+    type="button"
+    onClick={onClick}
+    role="checkbox"
+    aria-checked={checked}
+    className="flex items-center gap-[0.8rem] cursor-pointer"
+  >
+    <Icon name="check-filled" className={checked ? 'text-main-800' : 'text-gray-300'} />
+    <span className="body_16_m">{label}</span>
+  </button>
src/pages/sign-up/schema/validation-schema.ts (1)

14-43: 출생연도 유효 범위 검증 추가 + 소개문구 에러 메시지 명시

  • birthYear는 4자리 숫자만 검증되어 과거/미래 연도(예: 0000, 3000)도 통과합니다. 합리적 범위(예: 1900~현재년도)로 제한하세요.
  • introduction은 min/max만 있고 사용자 메시지가 없습니다. 간단한 안내 메시지를 추가하면 UX가 좋아집니다.
   birthYear: z
     .string()
     .min(4, { message: BIRTH_ERROR_MESSAGES.LENGTH })
     .max(4, { message: BIRTH_ERROR_MESSAGES.LENGTH })
-    .regex(/^\d+$/, { message: BIRTH_ERROR_MESSAGES.NUMBER }),
+    .regex(/^\d+$/, { message: BIRTH_ERROR_MESSAGES.NUMBER })
+    .refine((val) => {
+      const year = Number(val);
+      const now = new Date().getFullYear();
+      return year >= 1900 && year <= now;
+    }, { message: '유효한 출생 연도를 입력해 주세요.' }),
@@
-  introduction: z.string().trim().min(INTRODUCTION_MIN_LENGTH).max(INTRODUCTION_MAX_LENGTH),
+  introduction: z
+    .string()
+    .trim()
+    .min(INTRODUCTION_MIN_LENGTH, { message: '1자 이상 입력해 주세요.' })
+    .max(INTRODUCTION_MAX_LENGTH, { message: `최대 ${INTRODUCTION_MAX_LENGTH}자까지 입력 가능해요.` }),
src/pages/sign-up/components/signup-step.tsx (1)

155-160: 중복 제출 방지: 진행 중(disabled) 처리 추가

mutateAsync 연쇄 호출 중에는 버튼을 비활성화해 중복 API 호출을 방지하세요.

-      <Button
+      {/* 진행 중 상태 병합 */}
+      <Button
         label="가입하기"
         className="w-full"
         ariaLabel="가입하기"
         type="submit"
-        disabled={!isValid}
+        disabled={
+          !isValid ||
+          userInfoMutation.isPending ||
+          agreementInfoMutation.isPending
+        }
       />
🧹 Nitpick comments (8)
src/shared/constants/api.ts (1)

10-11: v2 엔드포인트 추가는 OK이지만, 상수 네이밍 컨벤션 일치 필요

동일 파일 내 GET_*, POST_* 접두어 컨벤션과 달리 AGREEMENT_INFO, USER_INFO는 HTTP 의도가 드러나지 않습니다. 후속 유지보수성을 위해 POST_AGREEMENT_CONSENT, POST_USER_INFO 등으로 맞추는 것을 권장합니다.

-  AGREEMENT_INFO: '/v2/users/consent',
-  USER_INFO: '/v2/users/info',
+  POST_AGREEMENT_CONSENT: '/v2/users/consent',
+  POST_USER_INFO: '/v2/users/info',
src/pages/sign-up/constants/LINK.ts (1)

1-3: 외부 링크 하드코딩 최소화

운영 중 링크 변경 가능성이 높으니 .env 혹은 서버에서 내려주는 설정으로 분리하면 핫픽스 비용을 줄일 수 있습니다. 최소한 주석으로 소스(노션 페이지 소유자)와 변경 절차를 명시해 주세요.

src/pages/sign-up/components/checkbox-row.tsx (1)

12-12: link?: string 타입 가드 보강 제안

잘못된 URL로 인한 런타임 이슈를 줄이려면 link?: \https://${string}` | `http://${string}`등의 템플릿 리터럴 타입으로 제한하거나, 내부에서try { new URL(link) }` 검증을 고려하세요.

src/shared/types/user-types.ts (1)

17-26: API 페이로드 스키마 정렬: gender 값 포맷 재확인

UI는 '여성'/'남성'을 사용 중인데, 서버가 동일 문자열을 기대하는지('FEMALE'/'MALE' 코드 대비)를 백엔드와 재확인해 주세요. 불일치 시 중앙 매핑 계층이 필요합니다.

서버가 한글 값을 그대로 받는 것이 확정이라면 아래처럼 명시적 타입으로 문서화해 두는 것을 권장합니다.

-export interface postUserInfoRequest {
+type Gender = '여성' | '남성';
+export interface postUserInfoRequest {
   nickname: string;
   introduction: string;
   birthYear: number;
-  gender: string;
+  gender: Gender;
 }

Also applies to: 29-35

src/shared/apis/user/user-mutations.ts (2)

15-16: 응답 타입 안전성 보강: post 제네릭 지정

post 래퍼에 제네릭을 명시해 응답 타입을 강제하세요. 추론 실패/any 누수 방지에 유효합니다.

-      mutationFn: ({ nickname, introduction, birthYear, gender }) =>
-        post(END_POINT.USER_INFO, { nickname, introduction, birthYear, gender }),
+      mutationFn: ({ nickname, introduction, birthYear, gender }) =>
+        post<responseTypes>(END_POINT.USER_INFO, { nickname, introduction, birthYear, gender }),
@@
-      mutationFn: ({ hasAccepted }) => post(END_POINT.AGREEMENT_INFO, { hasAccepted }),
+      mutationFn: ({ hasAccepted }) => post<responseTypes>(END_POINT.AGREEMENT_INFO, { hasAccepted }),

Also applies to: 29-29


17-24: 부가 제안: 실패 피드백 표면화

현재 onError는 console.error만 수행합니다. 토스트/알럿 등 사용자 피드백 계층을 주입(예: error boundary, 전역 toast)해 주세요. AGREEMENT_INFO에도 동일 권장.

원하시면 공통 errorHandler 유틸/토스트 훅을 제안 드립니다.

src/pages/sign-up/components/signup-step.tsx (2)

31-35: defaultValues의 gender: undefined 제거 (스키마와 타입 일치)

Zod 스키마상 gender는 필수 enum입니다. 초기값을 명시하지 않거나 빈 문자열/undefined를 UI 로직으로 처리하세요(현재 setValue로 설정하므로 생략 권장).

-    defaultValues: { nickname: '', gender: undefined, birthYear: '', introduction: '' },
+    defaultValues: { nickname: '', birthYear: '', introduction: '' },

51-65: 실패 시 사용자 피드백 추가 제안

catch 블록에서 console.error만 존재합니다. 약관 동의 실패/유저 정보 등록 실패를 구분해 사용자에게 명확한 메시지를 보여 주세요(토스트/인라인 에러).

필요 시 공통 에러 메시지 매핑과 토스트 훅 적용 패치를 제안 드립니다.

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 1be8cf3 and 8de7a2d.

📒 Files selected for processing (10)
  • src/pages/sign-up/components/agreement-step.tsx (2 hunks)
  • src/pages/sign-up/components/checkbox-row.tsx (2 hunks)
  • src/pages/sign-up/components/signup-step.tsx (4 hunks)
  • src/pages/sign-up/constants/LINK.ts (1 hunks)
  • src/pages/sign-up/constants/validation.ts (2 hunks)
  • src/pages/sign-up/schema/validation-schema.ts (2 hunks)
  • src/shared/apis/user/user-mutations.ts (1 hunks)
  • src/shared/constants/api.ts (1 hunks)
  • src/shared/constants/query-key.ts (1 hunks)
  • src/shared/types/user-types.ts (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#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/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 (4)
src/shared/apis/user/user-mutations.ts (7)
src/shared/types/base-types.ts (1)
  • responseTypes (7-10)
src/shared/types/user-types.ts (2)
  • postUserInfoRequest (21-26)
  • postAgreementInfoRequest (33-35)
src/shared/constants/query-key.ts (1)
  • USER_KEY (2-8)
src/shared/apis/base/http.ts (1)
  • post (7-9)
src/shared/constants/api.ts (1)
  • END_POINT (3-36)
src/shared/routes/router.tsx (1)
  • router (10-39)
src/shared/routes/routes-config.ts (1)
  • ROUTES (1-22)
src/pages/sign-up/schema/validation-schema.ts (1)
src/pages/sign-up/constants/validation.ts (2)
  • INTRODUCTION_MIN_LENGTH (41-41)
  • INTRODUCTION_MAX_LENGTH (42-42)
src/pages/sign-up/components/agreement-step.tsx (1)
src/pages/sign-up/constants/LINK.ts (2)
  • TERMS_LINK (1-1)
  • PRIVACY_LINK (3-3)
src/pages/sign-up/components/signup-step.tsx (5)
src/pages/sign-up/schema/validation-schema.ts (2)
  • UserInfoFormValues (45-45)
  • UserInfoSchema (14-43)
src/shared/apis/user/user-mutations.ts (1)
  • userMutations (11-31)
src/shared/types/user-types.ts (1)
  • postUserInfoRequest (21-26)
src/pages/sign-up/constants/validation.ts (2)
  • INTRODUCTION_PLACEHOLDER (25-26)
  • INTRODUCTION_MAX_LENGTH (42-42)
src/pages/sign-up/constants/NOTICE.ts (1)
  • INFORMATION_RULE_MESSAGE (17-17)
⏰ 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: Cloudflare Pages
🔇 Additional comments (2)
src/pages/sign-up/constants/validation.ts (2)

25-26: 플레이스홀더 개행 사용 확인 필요

플레이스홀더에 개행문자(\n)가 포함되어 있습니다. 단일 라인 <input>을 사용할 경우 줄바꿈이 무시되거나 레이아웃이 깨질 수 있습니다. 실제 사용 컴포넌트가 <textarea>인지 확인해 주세요.


41-42: 서버 스키마와 길이 제한(1–50자) 정합성 확인

백엔드 요청 스키마(예: zod/yup, OpenAPI)와 동일한 최소/최대 길이인지 확인 필요합니다. 불일치 시 서버 400 발생 가능성이 있습니다.

Comment on lines +55 to 56
link={TERMS_LINK}
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

외부 링크 보안 처리 및 클릭 전파 제어 확인 필요

CheckboxRow 내부에서 외부(노션) 링크를 열 때 다음을 보장해 주세요:

  • target="_blank" + rel="noopener noreferrer" 적용
  • 링크 클릭 시 체크 토글이 되지 않도록 e.stopPropagation()

CheckboxRow 구현에서 위 두 가지가 누락되어 있으면 추가해 주세요.

다음 스크립트로 CheckboxRow의 앵커 구현을 확인해 주세요.

추가로, 약관/개인정보 링크는 외부 도메인이므로 새 탭 열기 정책을 제품 측과 합의했는지도 확인 부탁드립니다.

Also applies to: 63-64


🏁 Script executed:

#!/bin/bash
# CheckboxRow에서 링크 처리(target/rel/stopPropagation) 확인
FILE="$(fd -a 'checkbox-row.tsx' | head -n1)"
echo "File: $FILE"
rg -n -C3 'href=|target=|rel=|stopPropagation' "$FILE"

Length of output: 604


CheckboxRow 외부 링크 클릭 시 이벤트 전파 차단 처리 필요

  • <a> 태그 onClick 핸들러에서 e.stopPropagation() 호출 추가
  • (참고) target="_blank"rel="noopener noreferrer"는 이미 적용되어 있습니다
  • 약관/개인정보 링크의 새 탭 열기 정책에 대해 제품 측 확인 필요
🤖 Prompt for AI Agents
In src/pages/sign-up/components/agreement-step.tsx around lines 55-56, the
external links inside the CheckboxRow are allowing click events to propagate and
trigger the row's selection; add an onClick handler to the <a> elements that
calls e.stopPropagation() (preserving existing target="_blank" and rel="noopener
noreferrer") so link clicks open the external page without toggling the
checkbox, and confirm with product whether links should continue opening in a
new tab.

Comment on lines +30 to +33
<a href={link} className="cursor-pointer" target="_blank" rel="noopener noreferrer">
{svg}
</a>
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

link 미지정 시 빈 앵커 렌더링 방지

link가 없을 때도 <a>가 렌더링되어 비어 있는 포커스 가능한 요소가 생깁니다. 접근성/탭 순서에 악영향을 줄 수 있으므로 조건부 렌더링으로 교체하세요.

-      <a href={link} className="cursor-pointer" target="_blank" rel="noopener noreferrer">
-        {svg}
-      </a>
+      {link ? (
+        <a href={link} className="cursor-pointer" target="_blank" rel="noopener noreferrer">
+          {svg}
+        </a>
+      ) : (
+        svg && <span aria-hidden>{svg}</span>
+      )}
📝 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.

Suggested change
<a href={link} className="cursor-pointer" target="_blank" rel="noopener noreferrer">
{svg}
</a>
</div>
{link ? (
<a
href={link}
className="cursor-pointer"
target="_blank"
rel="noopener noreferrer"
>
{svg}
</a>
) : (
svg && <span aria-hidden>{svg}</span>
)}
</div>
🤖 Prompt for AI Agents
In src/pages/sign-up/components/checkbox-row.tsx around lines 30 to 33, the code
always renders an <a> even when link is undefined which creates an empty,
focusable anchor; change to conditional rendering so that if link is truthy you
render the <a href={link} target="_blank" rel="noopener noreferrer"
className="cursor-pointer">{svg}</a>, otherwise render a non-interactive element
(e.g. a <span> or <div> with the same visual styling but no href or tabIndex) so
the SVG is not placed inside a focusable anchor when there is no link.

Comment on lines 47 to 48
const agreementInfoMutaion = useMutation(userMutations.AGREEEMENT_INFO());

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

오타 정정 및 식별자 일치: agreementInfoMutaion + AGREEEMENT_INFO

  • 변수: agreementInfoMutaion → agreementInfoMutation
  • userMutations 키: AGREEEMENT_INFO → AGREEMENT_INFO (user-mutations.ts 변경에 맞춰 수정)
-  const userInfoMutation = useMutation(userMutations.USER_INFO());
-  const agreementInfoMutaion = useMutation(userMutations.AGREEEMENT_INFO());
+  const userInfoMutation = useMutation(userMutations.USER_INFO());
+  const agreementInfoMutation = useMutation(userMutations.AGREEMENT_INFO());
@@
-      await agreementInfoMutaion.mutateAsync({ hasAccepted: true });
+      await agreementInfoMutation.mutateAsync({ hasAccepted: true });

Also applies to: 60-62

🤖 Prompt for AI Agents
In src/pages/sign-up/components/signup-step.tsx around lines 47-48 (and also
update occurrences at lines 60-62), rename the misspelled variable
agreementInfoMutaion to agreementInfoMutation and update the mutation key call
from userMutations.AGREEEMENT_INFO() to userMutations.AGREEMENT_INFO(); update
all references to the variable in this file to use the new name and ensure
imports/types remain correct so the code compiles.

Comment on lines 26 to 30
AGREEEMENT_INFO: () =>
mutationOptions<responseTypes, Error, postAgreementInfoRequest>({
mutationKey: USER_KEY.AGREEMENT(),
mutationFn: ({ hasAccepted }) => post(END_POINT.AGREEMENT_INFO, { hasAccepted }),
}),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

오타: AGREEEMENT_INFO(EEE) → AGREEMENT_INFO(EE)

오탈자 때문에 IDE 검색/자동완성/일관성이 떨어집니다. 공개 API 성격의 export 키는 특히 엄격히 관리해 주세요. 해당 식별자를 사용하는 모든 곳(예: signup-step.tsx) 함께 변경해야 합니다.

-  AGREEEMENT_INFO: () =>
+  AGREEMENT_INFO: () =>
     mutationOptions<responseTypes, Error, postAgreementInfoRequest>({
       mutationKey: USER_KEY.AGREEMENT(),
       mutationFn: ({ hasAccepted }) => post(END_POINT.AGREEMENT_INFO, { hasAccepted }),
     }),

아래 스크립트로 레포 전체의 오타 사용처를 찾아 일괄 수정해 주세요.


🏁 Script executed:

#!/bin/bash
rg -n "AGREEE(MENT|MENT_INFO|MENT\(\))" -S -C2

Length of output: 850


AGREEEMENT_INFO 식별자 철자 오류 및 사용처 일괄 수정

  • src/shared/apis/user/user-mutations.ts: AGREEEMENT_INFOAGREEMENT_INFO
  • src/pages/sign-up/components/signup-step.tsx: userMutations.AGREEEMENT_INFO()userMutations.AGREEMENT_INFO() (변수명 agreementInfoMutaionagreementInfoMutation도 함께 수정)
🤖 Prompt for AI Agents
In src/shared/apis/user/user-mutations.ts around lines 26 to 30, rename the
misspelled export AGREEEMENT_INFO to AGREEMENT_INFO (fix the identifier and any
references in its mutationKey/usage); then in
src/pages/sign-up/components/signup-step.tsx update any calls
userMutations.AGREEEMENT_INFO() to userMutations.AGREEMENT_INFO() and rename the
local variable agreementInfoMutaion to agreementInfoMutation accordingly so all
references and imports match the corrected identifier.

heesunee and others added 5 commits August 31, 2025 14:52
* style: footer 수정 (#305)

* style: 마이페이지 제목, 버튼 이름 수정 (#305)

* feat: divider 컴포넌트 추가 (#305)

* feat: divider 추가 (#305)

* feat: 문의하기, 의견 보내기 구글폼 연결 (#305)

* feat: logout api 추가 (#305)

* feat: 로그아웃 api 연결 (#305)

* feat: 로그아웃 요청 성공시 reload 추가 (#305)

* feat: 문의하기/의견보내기 링크 연결 (#305)

* refactor: 링크 이동 a 태그로 변경 (#305)

* feat: LOGOUT시 쿼리 삭제, 취소 (#305)

* feat: 로그아웃 커서 추가 (#305)

* feat: 로그아웃 api 실패시 로직 추가 (#307)

* feat: logout api 실패 로직 추가 (#305)

---------

Co-authored-by: heesunee <heesun729@uos.ac.kr>
* feat: 프로필 수정 상단 퍼블리싱 (#307)

* feat: 프로필 수정 내 응원팀 추가 (#307)

* feat: 매칭 조건 수정 리스트 추가 (#307)

* feat: 조건 수정 초기값 반영 (#307)

* feat: selectionGroup component 분리 (#307)

* feat: 클릭시 로직 추가 (#307)

* style: 불필요한 border-t 삭제 (#307)

* feat: SP1 회원가입 페이지 디자인 반영사항 적용 (#304)

* feat: signup-page funnel apply (#303)

* feat: 약관 동의 페이지 퍼블리싱 (#303)

* feat: arrow-right-18 svg add (#303)

* feat: input className 받게 확장 (#303)

* feat: 한줄소개 관련 유효성 추가 (#303)

* feat: input textarea 분기 추가 (#303)

* feat: 한줄소개 글자수 실시간 반영 (#303)

* feat: 서비스 이용약관 체크박스 로직 추가 (#303)

* feat: 이용약관 다음으로 이동 추가 (#303)

* style: 정보입력 페이지 pb 수정 (#303)

* feat: 체크박스 row 컴포넌트로 분리 (#303)

* feat: 이용약관 페이지 checkboxrow 컴포넌트로 교체 (#303)

* fix: 체크박스 클릭 함수 icon으로 이동 (#303)

* refactor: maxLength 추가 (#303)

* feat: 한줄소개 공백 입력 제거 (#303)

* feat: checkbox button으로 감싸게 수정 (#303)

---------

Co-authored-by: heesunee <heesun729@uos.ac.kr>

* feat: 응원팀 없어요 클릭시 메이트의 응원팀 disabled (#307)

* feat: 응원팀 없을때 기본 값 수정 (#307)

* feat: button disabled style add (#307)

* fix: 수정 취소 (#307)

* fix: 수정 취소 (#307)

* feat: SP1 마이페이지 수정 및 로그아웃 api 연결 (#306)

* style: footer 수정 (#305)

* style: 마이페이지 제목, 버튼 이름 수정 (#305)

* feat: divider 컴포넌트 추가 (#305)

* feat: divider 추가 (#305)

* feat: 문의하기, 의견 보내기 구글폼 연결 (#305)

* feat: logout api 추가 (#305)

* feat: 로그아웃 api 연결 (#305)

* feat: 로그아웃 요청 성공시 reload 추가 (#305)

* feat: 문의하기/의견보내기 링크 연결 (#305)

* refactor: 링크 이동 a 태그로 변경 (#305)

* feat: LOGOUT시 쿼리 삭제, 취소 (#305)

* feat: 로그아웃 커서 추가 (#305)

* feat: 로그아웃 api 실패시 로직 추가 (#307)

* feat: logout api 실패 로직 추가 (#305)

---------

Co-authored-by: heesunee <heesun729@uos.ac.kr>

* feat: 프로필 수정 상단 퍼블리싱 (#307)

* feat: 프로필 수정 내 응원팀 추가 (#307)

* feat: 매칭 조건 수정 리스트 추가 (#307)

* feat: 조건 수정 초기값 반영 (#307)

* feat: selectionGroup component 분리 (#307)

* feat: 클릭시 로직 추가 (#307)

* style: 불필요한 border-t 삭제 (#307)

* feat: 응원팀 없어요 클릭시 메이트의 응원팀 disabled (#307)

* feat: 응원팀 없을때 기본 값 수정 (#307)

* feat: button disabled style add (#307)

* fix: 수정 취소 (#307)

* fix: 수정 취소 (#307)

* feat: 프로필 수정 페이지 내 divider, 한줄 소개 input 수정(#307)

* feat: 리뷰 반영 (#307)

---------

Co-authored-by: heesunee <heesun729@uos.ac.kr>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Aug 31, 2025

Deploying mateball-client with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8f7b650
Status: ✅  Deploy successful!
Preview URL: https://1da6058a.mateball-client.pages.dev
Branch Preview URL: https://feat--323-sp1-signup-api.mateball-client.pages.dev

View logs

@heesunee heesunee merged commit 213f259 into develop Aug 31, 2025
6 checks passed
@heesunee heesunee deleted the feat/#323/sp1-signup-api branch August 31, 2025 08:55
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (12)
src/shared/components/footer/footer.tsx (1)

15-15: gap 축소 영향: 데스크톱에서 이전 여백감 유지하도록 반응형 보완 제안

모바일 축소는 타당해 보이나, md 이상에서는 종전(4.8rem) 유지가 더 안정적일 수 있습니다.

-      className={clsx('cap_12_m w-full flex-col gap-[2.4rem] px-[1.6rem] py-[3.2rem]', {
+      className={clsx('cap_12_m w-full flex-col gap-[2.4rem] md:gap-[4.8rem] px-[1.6rem] py-[3.2rem]', {
src/shared/components/card/match-card/styles/card-variants.ts (1)

12-12: outline 두께가 기본값으로 바뀌어 UI 변동 가능 — 1px 고정 복원 제안

명시적 두께(outline-[1px]) 제거로 브라우저/테일윈드 기본 두께(보통 2px)로 변해 보일 수 있습니다. 기존 1px 룩을 유지하려면 아래처럼 복원하는 편이 안전합니다.

-      active: 'bg-main-200 outline outline-main-600',
+      active: 'bg-main-200 outline outline-[1px] outline-main-600',
src/pages/edit-profile/constants/edit-profile.ts (1)

1-1: 리터럴 타입 보존으로 안전성 향상

옵션 배열에 as const를 붙여 유니온 리터럴로 쓰면 오타/불일치 방지가 쉬워집니다. 추후 선택값 타입으로 재사용도 용이합니다.

-export const PROFILE_SYNC_MATE = ['같은 팀 메이트', '상관없어요'];
+export const PROFILE_SYNC_MATE = ['같은 팀 메이트', '상관없어요'] as const;

아울러 필요 시 타입을 노출해 두면 좋습니다(파일 외 추가 코드):

export type ProfileSyncMateOption = typeof PROFILE_SYNC_MATE[number];
src/shared/components/button/button/styles/button-variants.ts (1)

14-15: 비활성 스타일에 상호작용 차단 클래스 추가 권장

cursor-pointer가 베이스 클래스에 있어 시각적으로만 비활성입니다. 포인터/클릭 차단 클래스를 함께 부여해 혼란을 줄이는 편이 좋습니다.

-        disabled: 'bg-gray-100 text-gray-400',
+        disabled: 'bg-gray-100 text-gray-400 cursor-not-allowed pointer-events-none',

추가로 컴포넌트 레벨에서 <button disabled>/aria-disabled도 함께 지정되는지 확인 부탁드립니다.

src/shared/components/header/header.tsx (1)

14-24: /match 하위 경로에도 bg-gray-100 적용되도록 isMatch 조건 확장

-  const isMatch = pathname === ROUTES.MATCH;
+  const isMatch =
+    pathname === ROUTES.MATCH || pathname.startsWith(`${ROUTES.MATCH}/`);
src/pages/edit-profile/mocks/mockEditData.ts (1)

1-6: 상수 재사용으로 문자열 드리프트 방지

teamAllowed: '상관없어요'PROFILE_SYNC_MATE와 중복입니다. 상수를 참조하면 i18n/카피 변경 시 드리프트를 막을 수 있습니다. 동시에 리터럴 보호를 위해 as const도 권장합니다.

+import { PROFILE_SYNC_MATE } from '../constants/edit-profile';
+
-export const mockEditData = {
+export const mockEditData = {
   team: '응원하는 팀이 없어요.',
-  teamAllowed: '상관없어요',
+  teamAllowed: PROFILE_SYNC_MATE[1],
   style: '직관먹방러',
   genderPreference: '여성',
-};
+} as const;
src/shared/components/divider/divider.tsx (1)

21-28: 접근성: role='separator'와 aria-orientation을 추가해 주세요

스크린 리더가 시각적 구분선을 더 잘 인지할 수 있도록 시맨틱 속성을 부여하는 것을 권장합니다.

   return (
     <div
-      className={clsx(
+      role="separator"
+      aria-orientation={isHorizontal ? 'horizontal' : 'vertical'}
+      className={clsx(
         isHorizontal ? `w-full ${margin || 'my-4'}` : `h-full ${margin || 'mx-4'}`,
         color,
         className,
       )}
       style={isHorizontal ? { height: `${thickness}rem` } : { width: `${thickness}rem` }}
     />
   );
src/pages/edit-profile/components/selection-group.tsx (1)

29-41: disabled 상태에서는 Button에도 진짜 disabled 속성을 전달하는 것이 안전합니다

시각적 비활성화뿐 아니라 포커스/키보드 입력도 차단하려면 Button 컴포넌트가 지원한다면 disabled prop을 함께 넘겨주세요.

           return (
             <Button
               key={key}
               label={label}
               variant={disabled ? 'disabled' : isSelected ? 'skyblue' : 'gray2'}
               className={cn(
                 'cap_14_sb w-auto px-[1.6rem] py-[0.6rem]',
                 disabled && 'cursor-not-allowed',
               )}
-              onClick={() => {
+              disabled={disabled}
+              onClick={() => {
                 if (disabled) return;
                 onSelect(label);
               }}
             />
           );

Button이 disabled prop을 지원하는지 확인 부탁드립니다. 지원하지 않는다면 추후 접근성 개선 이슈로 분리하는 것도 좋습니다.

src/pages/edit-profile/edit-profile.tsx (1)

66-74: 한 줄 소개 글자수 표시: length=0 하드코딩 대신 입력 길이를 연결

UI 정확도를 위해 상태 연결을 권장합니다. (API 연동 전에 간단히 useState로 반영)

-        <Input
+        {/* TODO: introduction 상태 연결 */}
+        <Input
           placeholder={INTRODUCTION_PLACEHOLDER}
           defaultMessage={INFORMATION_RULE_MESSAGE}
-          length={0}
+          length={0 /* introduction.length */}
           hasLength
           className="h-[10.4rem]"
           label="한 줄 소개"
           multiline
         />
src/pages/profile/profile.tsx (1)

62-70: 로그아웃 버튼 포커스 스타일 고려(선택사항)

키보드 접근성을 위해 포커스 가시성 클래스(예: focus:outline, focus:ring)를 추가하면 좋습니다.

-        <button
+        <button
           type="button"
           onClick={() => logout()}
           aria-label="로그아웃"
-          className="cap_14_m cursor-pointer py-[0.8rem] text-gray-800"
+          className="cap_14_m cursor-pointer py-[0.8rem] text-gray-800 focus:outline-none focus:ring-2 focus:ring-skyblue-400"
         >
src/shared/apis/user/user-mutations.ts (2)

15-17: post 제네릭 명시로 반환 타입 안정화

현재 post는 제네릭 T를 인자로 받는데 호출부에서 T가 유추되지 않을 수 있습니다. 명시적으로 responseTypes를 지정해 타입 안전성을 확보하세요.

- post(END_POINT.USER_INFO, { nickname, introduction, birthYear, gender }),
+ post<responseTypes>(END_POINT.USER_INFO, { nickname, introduction, birthYear, gender }),

- mutationFn: () => post(END_POINT.POST_AUTH_LOGOUT),
+ mutationFn: () => post<responseTypes>(END_POINT.POST_AUTH_LOGOUT),

- mutationFn: ({ hasAccepted }) => post(END_POINT.AGREEMENT_INFO, { hasAccepted }),
+ mutationFn: ({ hasAccepted }) => post<responseTypes>(END_POINT.AGREEMENT_INFO, { hasAccepted }),

Also applies to: 29-30, 43-44


21-23: 에러 처리 일관화(UX/로깅 표준화)

console.error만으로는 UX 피드백/관측이 약합니다. 공통 에러 핸들러(토스트+Sentry 등)로 래핑해 일관되게 처리하는 걸 권장합니다.

원하시면 reportApiError 유틸을 추가해 드릴게요.

Also applies to: 35-37

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 38452f1 and bf7bb50.

📒 Files selected for processing (18)
  • src/pages/edit-profile/components/selection-group.tsx (1 hunks)
  • src/pages/edit-profile/constants/edit-profile.ts (1 hunks)
  • src/pages/edit-profile/edit-profile.tsx (1 hunks)
  • src/pages/edit-profile/mocks/mockEditData.ts (1 hunks)
  • src/pages/profile/constants/link.ts (1 hunks)
  • src/pages/profile/edit-profile/edit-profile.tsx (0 hunks)
  • src/pages/profile/profile.tsx (2 hunks)
  • src/shared/apis/user/user-mutations.ts (1 hunks)
  • src/shared/components/button/button/styles/button-variants.ts (1 hunks)
  • src/shared/components/card/match-card/styles/card-variants.ts (1 hunks)
  • src/shared/components/divider/divider.tsx (1 hunks)
  • src/shared/components/footer/constants/legal.ts (0 hunks)
  • src/shared/components/footer/footer.tsx (2 hunks)
  • src/shared/components/header/header.tsx (1 hunks)
  • src/shared/components/header/utils/get-header.tsx (1 hunks)
  • src/shared/constants/api.ts (1 hunks)
  • src/shared/constants/query-key.ts (1 hunks)
  • src/shared/routes/lazy.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • src/shared/components/footer/constants/legal.ts
  • src/pages/profile/edit-profile/edit-profile.tsx
✅ Files skipped from review due to trivial changes (2)
  • src/shared/components/header/utils/get-header.tsx
  • src/pages/profile/constants/link.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/shared/constants/api.ts
  • src/shared/constants/query-key.ts
🧰 Additional context used
🧠 Learnings (2)
📓 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-06-29T18:02:42.616Z
Learnt from: heesunee
PR: MATEBALL/MATEBALL-CLIENT#7
File: src/shared/routes/Router.tsx:4-4
Timestamp: 2025-06-29T18:02:42.616Z
Learning: The Home component file in the MATEBALL-CLIENT project was renamed from Home.tsx to home.tsx, so the correct import path is 'pages/home/home'.

Applied to files:

  • src/shared/routes/lazy.ts
🧬 Code graph analysis (5)
src/shared/components/header/header.tsx (1)
src/shared/routes/routes-config.ts (1)
  • ROUTES (1-22)
src/pages/edit-profile/edit-profile.tsx (6)
src/pages/edit-profile/mocks/mockEditData.ts (1)
  • mockEditData (1-6)
src/pages/sign-up/constants/validation.ts (1)
  • NICKNAME_PLACEHOLDER (21-21)
src/pages/sign-up/constants/NOTICE.ts (2)
  • NICKNAME_RULE_MESSAGE (9-9)
  • INFORMATION_RULE_MESSAGE (17-17)
src/pages/onboarding/constants/onboarding.ts (4)
  • TEAMS (13-24)
  • NO_TEAM_OPTION (26-26)
  • VIEWING_STYLE (30-46)
  • GENDER (48-63)
src/pages/edit-profile/constants/edit-profile.ts (1)
  • PROFILE_SYNC_MATE (1-1)
src/shared/libs/cn.ts (1)
  • cn (4-6)
src/pages/profile/profile.tsx (4)
src/shared/apis/user/user-queries.ts (1)
  • userQueries (7-21)
src/shared/apis/user/user-mutations.ts (1)
  • userMutations (11-45)
src/shared/routes/routes-config.ts (1)
  • ROUTES (1-22)
src/pages/profile/constants/link.ts (2)
  • REQUEST_LINK (1-1)
  • FEEDBACK_LINK (3-3)
src/shared/apis/user/user-mutations.ts (6)
src/shared/types/base-types.ts (1)
  • responseTypes (7-10)
src/shared/types/user-types.ts (2)
  • postUserInfoRequest (21-26)
  • postAgreementInfoRequest (33-35)
src/shared/constants/query-key.ts (1)
  • USER_KEY (2-10)
src/shared/apis/base/http.ts (1)
  • post (7-9)
src/shared/constants/api.ts (1)
  • END_POINT (3-39)
src/shared/routes/routes-config.ts (1)
  • ROUTES (1-22)
src/pages/edit-profile/components/selection-group.tsx (1)
src/shared/libs/cn.ts (1)
  • cn (4-6)
🪛 GitHub Check: build-and-deploy
src/pages/edit-profile/edit-profile.tsx

[failure] 15-15:
'"@pages/sign-up/constants/validation"' has no exported member named 'INFORMATION_PLACEHOLDER'. Did you mean 'INTRODUCTION_PLACEHOLDER'?

🪛 GitHub Actions: Build & Deploy
src/pages/edit-profile/edit-profile.tsx

[error] 15-15: During 'npm run build' (tsc -b && vite build): TS2724: "@pages/sign-up/constants/validation" has no exported member named 'INFORMATION_PLACEHOLDER'. Did you mean 'INTRODUCTION_PLACEHOLDER'?

🔇 Additional comments (6)
src/shared/components/footer/footer.tsx (1)

1-1: MATCHING_PLATFORM_NOTICE 전역 사용처 없음 확인
코드베이스 전체 검색 결과 해당 식별자가 더 이상 사용되지 않음을 확인했습니다. 승인합니다.

src/shared/routes/lazy.ts (1)

22-22: 경로 정정 LGTM

EditProfile 경로를 @pages/edit-profile/edit-profile로 정리한 변경, 라우팅 일관성 측면에서 적절합니다.

src/pages/profile/profile.tsx (1)

36-41: 프로필 편집 이동 및 UI 구획 정리 LGTM

라우팅/분리선/외부 링크 섹션 추가가 명확합니다. 접근성 속성(rel, aria-label)도 적절합니다.

src/shared/apis/user/user-mutations.ts (3)

40-44: LGTM — 약관 동의 mutation 구성 적절

키/엔드포인트/페이로드(shape)가 명확합니다. 가입 단계에서 mutateAsync로 선행 호출하기에 적합합니다.


10-10: LGTM — 타입 import 정합성

API 스펙과 맞는 타입(postAgreementInfoRequest, postUserInfoRequest) 사용 확인했습니다.


31-35: 글로벌 캐시 초기화 도입 전 영향 범위 검토

레포에는 userQueries 외에 matchQueries, gameQueries 등 다양한 도메인 캐시가 존재합니다. queryClient.clear()를 적용하면 이들 캐시도 모두 초기화되어 성능 저하나 의도치 않은 동작이 발생할 수 있으므로, 글로벌 초기화가 적합한지 확인하세요. 유지해야 할 캐시가 없다면 전역 초기화를, 특정 도메인만 초기화해야 한다면 기존 cancelQueries/removeQueries 방식을 유지하거나 선택적 제거 로직을 고려해야 합니다.

- await queryClient.cancelQueries({ queryKey: USER_KEY.ALL });
- queryClient.removeQueries({ queryKey: USER_KEY.ALL });
+ await queryClient.cancelQueries();   // 전역 취소
+ queryClient.clear();                 // 전역 캐시 초기화

Comment on lines +21 to +23
const [mateTeam, setMateTeam] = useState(mockEditData.teamAllowed || '상관없어요');
const [viewStyle, setViewStyle] = useState(mockEditData.style);
const [isSubmit, setIsSubmit] = useState(false);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

초기값 불일치로 인한 상시 Dirty 상태 발생 가능

state는 mateTeam을 mockEditData.teamAllowed || '상관없어요'로 초기화하지만, initialValue는 mockEditData.teamAllowed만 저장합니다. teamAllowed가 undefined일 때 즉시 dirty로 평가됩니다. 둘을 일치시켜 주세요.

   const initialValue = useRef({
     team: mockEditData.team,
     gender: mockEditData.genderPreference,
-    mateTeam: mockEditData.teamAllowed,
+    mateTeam: mockEditData.teamAllowed || '상관없어요',
     viewStyle: mockEditData.style,
   });

Also applies to: 25-30, 32-41

🤖 Prompt for AI Agents
In src/pages/edit-profile/edit-profile.tsx around lines 21-23 (and similarly
lines 25-30, 32-41), component state initializers use fallbacks (e.g., mateTeam
= mockEditData.teamAllowed || '상관없어요') while the form initialValue(s) use
mockEditData.* directly, causing fields to be dirty when the data is undefined;
fix by making the form initialValues and corresponding useState initializers
consistent — either apply the same fallback in the form initialValues (e.g., use
mockEditData.teamAllowed || '상관없어요') or remove the fallback from useState and
initialize both from a single normalized source (compute normalizedInitial = {
teamAllowed: mockEditData.teamAllowed || '상관없어요', ... } and use it for both
state and form initialValues).

Comment on lines +45 to +51
const handleSaveClick = () => {
if (!isDirty) return;

setIsSubmit(true);

// TODO: 실제 API 호출
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

isSubmit 플래그가 해제되지 않아 버튼이 영구 비활성화될 수 있음

API 연동 전이라도 finally에서 해제해 두는 것이 안전합니다.

 const handleSaveClick = () => {
   if (!isDirty) return;

   setIsSubmit(true);

-  // TODO: 실제 API 호출
+  // TODO: 실제 API 호출
+  try {
+    // await apiCall();
+  } finally {
+    setIsSubmit(false);
+  }
 };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/pages/edit-profile/edit-profile.tsx around lines 45 to 51, the handler
sets isSubmit but never resets it, which can permanently disable the button;
make handleSaveClick async and wrap the API call (or placeholder) in a
try/catch/finally block, keep setIsSubmit(true) at the start and ensure
setIsSubmit(false) is called in finally so the flag is always cleared even if
the API is not yet implemented or an error occurs.

Comment on lines +4 to +6
import queryClient from '@libs/query-client';
import { router } from '@routes/router';
import { ROUTES } from '@routes/routes-config';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

API 레이어에서 라우터 의존성 분리 제안

공유 mutation 내부에서 라우팅이 일어나면 재사용 시(예: 프로필 수정) 의도치 않은 홈 리다이렉트가 발생할 수 있습니다. 네비게이션은 호출 컴포넌트(가입 플로우)로 올리고, 여기서는 캐시 무효화까지만 수행하는 편이 안전합니다.

원하시면 가입 전용 훅(useSignupUserInfoMutation)으로 분리하는 패치도 드리겠습니다.

적용 예시:

- import { router } from '@routes/router';
- import { ROUTES } from '@routes/routes-config';
...
   onSuccess: async () => {
     await queryClient.invalidateQueries({ queryKey: USER_KEY.ALL });
-    router.navigate(ROUTES.HOME, { replace: true });
   },

사용처 점검 스크립트:

Also applies to: 17-21


🏁 Script executed:

#!/bin/bash
# USER_INFO mutation 사용처가 가입 화면 외에 있는지 점검
rg -nP --type=ts 'userMutations\.USER_INFO\(\)' -C2

Length of output: 520


API 레이어에서 네비게이션 로직 제거
src/shared/apis/user/user-mutations.ts에서 router/ROUTES import와 router.navigate 호출을 삭제하고 캐시 무효화만 수행하도록 변경하세요. 네비게이션은 src/pages/sign-up/components/signup-step.tsxuseMutation onSuccess 콜백으로 이동해야 합니다.

// src/shared/apis/user/user-mutations.ts
- import { router } from '@routes/router';
- import { ROUTES } from '@routes/routes-config';
   onSuccess: async () => {
     await queryClient.invalidateQueries({ queryKey: USER_KEY.ALL });
-    router.navigate(ROUTES.HOME, { replace: true });
   },
// src/pages/sign-up/components/signup-step.tsx
const userInfoMutation = useMutation(userMutations.USER_INFO(), {
+ onSuccess: () => router.navigate(ROUTES.HOME, { replace: true }),
});
📝 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.

Suggested change
import queryClient from '@libs/query-client';
import { router } from '@routes/router';
import { ROUTES } from '@routes/routes-config';
import queryClient from '@libs/query-client';
🤖 Prompt for AI Agents
In src/shared/apis/user/user-mutations.ts around lines 4 to 6, remove the router
and ROUTES imports and delete any router.navigate calls so the module only
performs cache invalidation (e.g., using queryClient.invalidateQueries or
queryClient.refetchQueries) on mutation success; keep the queryClient import and
ensure the mutation success handlers only handle cache updates/invalidations.
Then move the navigation responsibility to the caller: implement
router.navigate(ROUTES.SOME_ROUTE) inside the useMutation onSuccess callback in
src/pages/sign-up/components/signup-step.tsx instead of doing navigation here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat new feature 희선

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 회원가입 페이지 api를 연결합니다.

1 participant