Conversation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
키보드 상태에 따라 버튼 스타일 자동 조정 Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Checkmark/Eye/EyeOff 아이콘 추가 Co-authored-by: Cursor <cursoragent@cursor.com>
PASSWORD_RULES에 HAS_LETTER, HAS_NUMBER 정규식 추가 - HAS_LETTER: 영문 포함 여부 검사 (/[A-Za-z]/) - HAS_NUMBER: 숫자 포함 여부 검사 (/\d/) 회원가입 폼에서 실시간 비밀번호 유효성 피드백을 위해 사용 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
앱 루트에 KeyboardProvider 추가 KeyboardAdaptiveButton에 enabled prop 추가 enabled=false 시 KeyboardStickyView 비활성화되어 BottomSheet와 레이어 충돌 문제 해결 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
useStepper: 스텝 기반 폼 네비게이션 훅 ANIMATION: duration/delay 상수 (fast, normal, slow, short, medium, long) 다단계 폼에서 재사용 가능한 스텝 관리 로직 분리 매직 넘버 제거를 위한 애니메이션 상수 중앙화 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
signUpFormSchema 및 SignUpFormData 타입 추가 register, verifyEmail, resendVerification 메서드 구현 verifyEmail 성공 시 토큰을 storage에 저장 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
registerMutationOptions: 회원가입 요청 verifyEmailMutationOptions: 이메일 인증 후 세션 설정 resendVerificationMutationOptions: 인증 코드 재발송 useCooldown: 재발송 버튼 쿨다운 타이머 관리 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
SignUpUserInfoForm: 닉네임/이메일 입력, 도메인 자동완성 SignUpPasswordForm: 비밀번호 설정, 실시간 규칙 검증 표시 SignUpVerificationForm: 6자리 OTP 입력, 자동 제출 TermsBottomSheet: 필수/선택 약관 동의 각 폼은 useStepper로 내부 스텝 관리 FormProvider로 폼 상태 공유 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
sign-up: 3단계 회원가입 (정보입력 → 비밀번호 → 인증) verify-email: 독립 이메일 인증 화면 Stack.Screen에 sign-up, verify-email 추가 로그인 화면 회원가입 버튼 연결 이메일 로그인 시 미인증 사용자 verify-email로 리다이렉트 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- watch → useWatch 리팩토링 (react-hook-form 권장 패턴) - useAppToast 네임스페이스 패턴 적용 (toast.error()) - 에러 코드 상수 사용 (ErrorCode.EMAIL_0501, EMAIL_0503) - 이미 가입된 이메일 에러 시 로그인 화면 이동 액션 추가 - placeholder 문구 및 애니메이션 타이밍 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary of ChangesHello @hijjoy, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 Pull Request는 모바일 앱에 이메일 기반의 회원가입 기능을 완전히 통합합니다. 사용자 경험을 고려하여 다단계 폼, 실시간 유효성 검증, 그리고 키보드에 반응하는 UI 요소를 포함하고 있습니다. 이를 통해 사용자는 앱 내에서 원활하게 계정을 생성하고 이메일 인증을 완료할 수 있습니다. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
이메일 회원가입 기능 구현을 위한 PR 잘 보았습니다. 전반적으로 새로운 기능들이 체계적으로 구현되었고, 컴포넌트 분리와 상태 관리, 에러 핸들링 등 좋은 패턴들이 많이 적용되었습니다. 몇 가지 코드 개선점을 제안드립니다. SignUpUserInfoForm의 이메일 도메인 추천 로직에서 발생할 수 있는 버그를 수정하고, Input 컴포넌트의 스타일링을 프로젝트 컨벤션에 맞게 개선하는 것을 제안합니다. 또한 sign-up 화면의 헤더 제목 로직을 더 간결하게 만들 수 있는 방법도 포함했습니다. 코드 리뷰를 확인해주세요.
apps/mobile/src/features/auth/presentations/components/SignUpUserInfoForm.tsx
Show resolved
Hide resolved
- getHeaderTitle() 함수를 SIGN_UP_STEP_TITLES 상수 객체로 변경 - satisfies Record<SignUpStep, string>으로 누락 방지 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
dydals3440
left a comment
There was a problem hiding this comment.
몇가지 코멘트랑 궁금점을 남겨보았습니다~!!!!! 확인하시구 이상없으시면 머지해주세요!!
|
|
||
| export const signUpFormSchema = z | ||
| .object({ | ||
| email: emailSchema, |
There was a problem hiding this comment.
프론트엔드의 도메인 모델링을 하기 위해서 *.model.ts파일을 만들어 뒀는데 서버 계약(@aido/validators)은 repository에서 요청과 응답에 대한 검증으로만 끝내야 할 것 같습니다!
흐름 요약
Repository Service Model
│ │ │
│ DTO (서버 계약) │ │
├──────────────────→│ │
│ │ Mapper │
│ ├─────────────────→│
│ │ │ Domain (순수)
@aido/validators → Repository에서만 사용
Domain Model → 서버 의존 없음 There was a problem hiding this comment.
폼 검증이 서버 규칙과 동일하게 관리되면 좋겠다고 생각해서 해당 부분을 계속 이렇게 짰던 것 같습니다 .. 😢
- 프론트 앱은 빌드된 코드라서, 서버가 규칙을 바꿔도 앱이 업데이트되지 않으면 그 규칙은 바뀌지 않음
- 어짜피 UI에 “8자리”가 하드코딩된 부분은 모두 수동으로 변경해야 함
제가 위 2가지를 생각하지 못했던 것 같습니다. 2번이 해결될 것 이라 생각했는데 그러려면
차라리 프론트에서 MIN_PASSWORD_LENGTH 같은 값을 한 곳에서 공유하고 스키마도 이것을 참조해서 프론트에서 만드는 것이 한번에 수정할 수 있는 방향인 것 같아요
수정해보겠습니다 ! 🧹🫧
There was a problem hiding this comment.
너무 오버 엔지니어링 같긴한데.. 사실 요런 폼 검증은 진짜 같이 쓸때 더 좋다고 생각해서..! 이부분은 한번 더 고민해보는걸로 해서 결정하면 될 것 같습니다!!
There was a problem hiding this comment.
일단 아래 제안해주신 model 파일에서 스키마는 분리하였습니다! (폼 검증은 presentation레이어이기 때문)
@aido/validators를 presentation 폼 검증에서 사용하는 것을 조금 더 고민해보고있는데,.....
공유해도/안해도 둘다 뭔가 서버에서 변경될 경우 문제는 동일하게 발생하지만 문제가 발생했을때 문제의 원인을 찾고 해결하는 방식이 다를 것 같기도해서
이 부분은 조금 더 고민해보고싶어서 일단은 남겨두겠습니다!
apps/mobile/src/features/auth/presentations/components/SignUpUserInfoForm.tsx
Outdated
Show resolved
Hide resolved
apps/mobile/src/features/auth/presentations/components/SignUpUserInfoForm.tsx
Show resolved
Hide resolved
apps/mobile/src/features/auth/presentations/components/TermsBottomSheet.tsx
Show resolved
Hide resolved
| @@ -0,0 +1,10 @@ | |||
| import { type Dispatch, type SetStateAction, useState } from 'react'; | |||
|
|
|||
| export const useStepper = <T extends readonly string[]>(steps: T) => { | |||
There was a problem hiding this comment.
조금 더 type-safe하게 관리할 수 있게 아래처럼 써보는건 어떨까요??
export const useStepper = <T extends readonly string[]>(steps: T) => {
const initialStep = steps[0];
const [step, setStep] = useState<T[number]>(initialStep);
const Step = React.memo((props: StepProps<T[number]>): ReactNode => {
return React.createElement(React.Fragment, null, props.children);
});
const Stepper = React.memo(({ children }: StepperProps<T[number]>) => {
const targetStep = children.find(
(childStep) => childStep.props.name === step,
);
return React.createElement(React.Fragment, null, targetStep);
});
return { Stepper, Step, setStep, step };
};지금도 괜찮지만 단계가 많아지고 단계 이름과 UI를 한 곳에서 선언적으로 관리하고 싶다면 살짝 다듬어 본다면, 요게 조금 더 좋을 수 있을 것 같아요!
export type StepType = typeof SIGNUP_REGISTRATION_STEPS;
const SIGNUP_REGISTRATION_STEPS = ["이름_입력", "이메일_입력", "비밀번호_입력", "비밀번호_확인", "전화번호_입력"] as const;
const { Stepper, Step, setStep, step } = useStepper<StepType>(SIGNUP_REGISTRATION_STEPS);
return (
<Stepper>
<Step name="이름_입력">이름_입력</Step>
<Step name="이메일_입력">이메일_입력</Step>
<Step name="비밀번호_입력">비밀번호_입력</Step>
<Step name="비밀번호_확인">비밀번호_확인</Step>
<Step name="전화번호_입력">전화번호_입력</Step>
</Stepper>
);There was a problem hiding this comment.
{match(step)
.with('정보_입력', () => <SignUpUserInfoForm ... />)
.with('비밀번호_설정', () => <SignUpPasswordForm ... />)
.with('이메일_인증', () => <SignUpVerificationForm />)
.exhaustive()}<Stepper>
<Step name="정보_입력"><SignUpUserInfoForm ... /></Step>
<Step name="비밀번호_설정"><SignUpPasswordForm ... /></Step>
<Step name="이메일_인증"><SignUpVerificationForm /></Step>
</Stepper>Stepper/Step는 UI를 선언적으로 나열하는 장점은 있지만, 모든 step을 다 처리했는지까지 타입으로 강제하진 못한다고생각합니다!
반대로 ts-pattern exhaustive 으로만 처리한 것은 모든 스탭이 처리되었는지 확인은 가능하지만 선언적으로 보이지 않고 조건문느낌이 큽니다
title부분을 ts-pattern으로 하고 Stepper/Step를 쓰는 방법으로는 가능하겠지만 이전 제미나이 리뷰에서 렌더링 시마다 함수를 호출하고 match 표현식을 평가하는 비용을 줄이기 위해 상수로 선언할 것을 제안하여 title은 Record<SignUpStep, string>로 정의했었어요.
회원가입부분은 나중에 새롭게 스탭이 생기거나 할 확률도 적기에 사실 스탭을 놓치거나 할 확률이 적지만
어떻게 step을 관리할까 조금 더 고민해볼 여지가 있다고생각하여 이 부분은 todo에 남겨두도록하겠습니다!
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 이벤트 핸들러 내부 setTimeout을 useEffect로 이동 - 클린업 함수로 타이머 정리 보장 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- auth.model.ts → user.model.ts, auth-tokens.model.ts, auth.policy.ts - SignUpFormData 스키마를 presentations/schemas로 이동 - 도메인 모델과 프레젠테이션 레이어 관심사 분리 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📋 개요
이메일 회원가입 플로우 전체 구현
약관 동의가 끝나면 회원가입 요청이 가서 실제 회원가입이 완료되는데
이후 인증코드를 받지 않고 이탈하면 로그인할 수 없게 됩니다
따라서 로그인 시 해당 에러가 발생하면 이메일 인증 번호를 할 수 있는 페이지로 이동하였습니다.
🏷️ 변경 유형
feat- 새로운 기능 추가refactor- 코드 리팩토링📦 영향 범위
apps/mobile- Expo 모바일 앱packages/validators- Zod 스키마📝 변경 내용
화면 구현
sign-up.tsx) - 3단계 스텝 폼폼 컴포넌트
SignUpUserInfoForm- 닉네임, 이메일 입력 + 도메인 추천SignUpPasswordForm- 비밀번호 설정 + 규칙 표시SignUpVerificationForm- 인증 코드 입력TermsBottomSheet- 약관 동의 바텀시트API 연동
register)verifyEmail)resendVerification)유효성 검증 & UX
공통 컴포넌트
KeyboardAdaptiveButton- 키보드 대응 버튼Input컴포넌트 추가useStepper훅 추가테스트 결과
✅ 체크리스트
작성자 확인
pnpm check(Biome) 검사를 통과했습니다pnpm test)pnpm build)🔗 관련 이슈
Closes #58
💬 추가 정보
⭐️ 구체적인 사용성은 추후 더 해결해 나가야할 예정 ⭐️