Skip to content

[MAT-99] Onboarding flow 개선#258

Merged
sterdsterd merged 12 commits intodevelopfrom
fix/mat-99-onboarding-flow
Mar 30, 2026
Merged

[MAT-99] Onboarding flow 개선#258
sterdsterd merged 12 commits intodevelopfrom
fix/mat-99-onboarding-flow

Conversation

@sterdsterd
Copy link
Copy Markdown
Collaborator

📌 Related Issue Number


✅ Key Changes

이번 PR에서 작업한 내용을 간략히 설명해주세요

  1. 회원가입/로그인 플로우를 2단계로 분리 — STEP 1(계정 생성: 이메일 입력, 약관동의, 본인인증)은 Auth 스택에서 풀스크린으로 처리하고, STEP 2(프로필 정보 입력: 학년, 선택과목, 학교, 수학등급)는 기존 온보딩 플로우에서 처리
  2. 기존 바텀시트 기반 UI(TermsConsentSheet, EmailAuthSheet)를 모두 풀스크린 네비게이션으로 전환 — 이메일 로그인, 비밀번호 찾기, 회원가입 플로우 포함
  3. signupStore를 신규 생성하여 STEP 1 데이터를 로컬에 영속 저장하고, 앱 재시작 시 진행 단계를 복원할 수 있도록 구현
  4. 온보딩에서 이메일/본인인증 스텝을 제거하고 프로필 입력(학년→선택과목→학교→수학등급)만 남도록 축소
  5. RootNavigator에서 sessionStatus + step1Completed + onboardingStatus 조합으로 Auth/Onboarding/메인 화면 분기 처리
  6. 비밀번호 찾기 플로우의 인증코드 검증 API 타입 오류 수정 (VerifyCodeRequest 타입 사용)
  7. 텍스트 스타일을 디자인 시스템 타이포그래피 토큰(typo-*)으로 통일

sterdsterd and others added 10 commits March 29, 2026 11:28
- Create signupStore for STEP 1 data (email, identity, terms) with AsyncStorage persistence
- Refactor onboardingStore to remove email/identity (moved to signupStore), add currentStep persistence
- Add signup/onboarding store reset on signOut to prevent data leaks between accounts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- EmailInputScreen: email input with existence check
- EmailLoginScreen: password login for existing users
- SignupPasswordScreen: password setup for new email users
- SignupEmailScreen: email collection for social signup
- SignupTermsScreen: terms consent (fullscreen, replaces bottom sheet)
- SignupIdentityScreen: SMS identity verification (moved from onboarding)
- ForgotEmail/ForgotCode/ForgotResetScreen: password reset flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove TermsConsentSheet and EmailAuthSheet from LoginScreen
- Social buttons now trigger OAuth directly without prior terms consent
- Email button navigates to fullscreen EmailInput screen
- Update useNativeOAuth to navigate to SignupEmail for first-time social users
- Register all new auth screens in AuthNavigator with proper param types
- Remove useEmailAuth exports (logic moved to individual screens)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove Email and Identity steps from onboarding (moved to auth signup)
- OnboardingScreen now starts from Grade step
- Update types to remove Email/Identity from OnboardingStackParamList
- WelcomeStep now merges signupStore (STEP 1) + onboardingStore (STEP 2) for postRegister
- Add currentStep persistence to all onboarding steps for app restart recovery
- Reset signupStore after successful registration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… flow

- RootNavigator: authenticated + STEP 1 incomplete → Auth stack (signup screens)
- RootNavigator: authenticated + STEP 1 complete → StudentNavigator (onboarding check)
- useLoadAssets: wait for Zustand persist store hydration before auth check
- useLoadAssets: auto sign-out on app restart if STEP 1 was incomplete
- StudentNavigator: clarify onboarding display condition comment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete EmailAuthSheet (replaced by fullscreen screens)
- Delete TermsConsentSheet (replaced by SignupTermsScreen)
- Delete useEmailAuth hook (logic distributed to individual screens)
- Delete EmailStep, IdentityStep from onboarding (moved to auth signup)
- Delete NicknameStep (was already deprecated)
- Remove unused imports (ActivityIndicator) from new screens

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rify

- postPasswordResetVerifyCode was using ResetPasswordRequest (requires newPassword)
  instead of VerifyCodeRequest (only email + code)
- Remove unnecessary newPassword: '' workaround in ForgotCodeScreen

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced hardcoded text styles with shared typography tokens
to ensure consistent design across the app.
Replaced all instances of TextInput with the shared OnboardingInput
component across authentication screens for consistency and improved
code reuse.
Add unique keys to navigation screens in RootNavigator to ensure
the stack resets when sessionStatus or onboardingStatus changes.
@sterdsterd sterdsterd requested a review from Copilot March 30, 2026 02:45
@sterdsterd sterdsterd self-assigned this Mar 30, 2026
@sterdsterd sterdsterd added the ✨ Feature 기능 개발 label Mar 30, 2026
@linear
Copy link
Copy Markdown

linear bot commented Mar 30, 2026

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pointer-admin Ready Ready Preview, Comment Mar 30, 2026 6:15am

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

학생 Auth/Onboarding 진입 구조를 “STEP 1(계정 생성/약관/본인인증) → STEP 2(프로필 온보딩)”로 재구성하고, 기존 바텀시트 기반 로그인/가입 UI를 풀스크린 네비게이션으로 전환하는 변경입니다.

Changes:

  • Auth 스택에 이메일 로그인/회원가입/비밀번호 찾기 풀스크린 플로우 추가 및 기존 바텀시트 구현 제거
  • signupStore / onboardingStore persist 도입 및 RootNavigator에서 session + step1Completed + onboardingStatus 기반 분기
  • 온보딩 STEP 2 스텝 단순화(이메일/본인인증 제거) 및 타이포 토큰(typo-*) 적용 확대

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
apps/native/src/stores/authStore.ts signOut 시 signup/onboarding 스토어 reset 연동
apps/native/src/navigation/student/StudentNavigator.tsx onboarding 표시 조건 주석/로직 보완
apps/native/src/navigation/auth/AuthNavigator.tsx Auth 스택에 이메일 로그인/가입/비번찾기 라우트 추가
apps/native/src/navigation/RootNavigator.tsx sessionStatus + step1Completed + onboardingStatus로 루트 분기
apps/native/src/hooks/useLoadAssets.ts persist hydration 대기 및 특정 조건에서 재시작 시 signOut
apps/native/src/features/student/onboarding/store/useOnboardingStore.ts onboarding store를 persist로 전환, currentStep/status 구조 변경
apps/native/src/features/student/onboarding/screens/types.ts 온보딩 라우트에서 Email/Identity 제거
apps/native/src/features/student/onboarding/screens/steps/WelcomeStep.tsx register payload를 signupStore(step1Data)+onboarding payload로 구성, signup reset
apps/native/src/features/student/onboarding/screens/steps/ScoreStep.tsx 다음/스킵 시 currentStep 갱신 추가, import alias 정리
apps/native/src/features/student/onboarding/screens/steps/SchoolStep.tsx 다음/스킵 시 currentStep 갱신 추가, 타이포 토큰 적용
apps/native/src/features/student/onboarding/screens/steps/NicknameStep.tsx 사용 중지된 닉네임 스텝 파일 제거
apps/native/src/features/student/onboarding/screens/steps/MathSubjectStep.tsx 다음 이동 시 currentStep 갱신 추가
apps/native/src/features/student/onboarding/screens/steps/GradeStep.tsx 다음 이동 시 currentStep 갱신 추가
apps/native/src/features/student/onboarding/screens/OnboardingScreen.tsx initialRouteName을 persist된 currentStep 기반으로 설정
apps/native/src/features/student/onboarding/components/OptionButton.tsx 타이포 토큰 적용
apps/native/src/features/student/onboarding/components/OnboardingLayout.tsx CTA/skip/back UI 타이포/스타일 토큰 정리
apps/native/src/features/student/onboarding/components/OnboardingInput.tsx 라벨/힌트/에러/성공 텍스트 타이포 토큰 적용
apps/native/src/features/student/onboarding/components/MailBoxGraphic.tsx SVG path/gradient/mask 리팩터링(에셋 업데이트)
apps/native/src/features/student/onboarding/components/InfoCard.tsx 타이포 토큰 적용
apps/native/src/features/auth/signup/store/useSignupStore.ts STEP 1 데이터 영속화(persist)용 signupStore 신규 추가
apps/native/src/features/auth/signup/screens/SignupTermsScreen.tsx 약관 동의 풀스크린 화면 신규
apps/native/src/features/auth/signup/screens/SignupPasswordScreen.tsx 이메일 가입 비밀번호 설정 화면 신규
apps/native/src/features/auth/signup/screens/SignupIdentityScreen.tsx 본인인증을 Auth 플로우로 이동 및 step1Completed 처리
apps/native/src/features/auth/signup/screens/SignupEmailScreen.tsx 소셜 가입 시 이메일 수집 화면 신규
apps/native/src/features/auth/signup/screens/ForgotResetScreen.tsx 비밀번호 재설정(새 비밀번호) 화면 신규
apps/native/src/features/auth/signup/screens/ForgotEmailScreen.tsx 비밀번호 찾기(코드 전송) 화면 신규
apps/native/src/features/auth/signup/screens/ForgotCodeScreen.tsx 비밀번호 찾기(코드 검증) 화면 신규
apps/native/src/features/auth/signup/screens/EmailLoginScreen.tsx 이메일 로그인(비밀번호 입력) 화면 신규
apps/native/src/features/auth/signup/screens/EmailInputScreen.tsx 이메일 존재 여부 판단 후 로그인/가입 분기 화면 신규
apps/native/src/features/auth/signup/index.ts signup 기능 엔트리(export) 추가
apps/native/src/features/auth/login/screens/LoginScreen.tsx 바텀시트 제거 및 EmailInput으로 네비게이션 전환
apps/native/src/features/auth/login/hooks/useNativeOAuth.ts 소셜 로그인 성공 시 signup 플로우 진입 처리 추가
apps/native/src/features/auth/login/hooks/useEmailAuth.ts 바텀시트 기반 이메일 Auth hook 제거
apps/native/src/features/auth/login/hooks/index.ts useEmailAuth export 제거
apps/native/src/features/auth/login/components/TermsConsentSheet.tsx 약관 바텀시트 컴포넌트 제거
apps/native/src/features/auth/login/components/EmailAuthSheet.tsx 이메일 Auth 바텀시트 컴포넌트 제거
apps/native/src/apis/controller/student/auth/postPasswordResetVerifyCode.ts VerifyCodeRequest 타입으로 수정
Comments suppressed due to low confidence (3)

apps/native/src/features/auth/signup/screens/SignupEmailScreen.tsx:57

  • 뒤로가기에서 useAuthStore.getState().signOut(); navigation.goBack();를 동기적으로 호출하고 있는데, signOut()은 async라 완료 전에 화면을 pop하고 곧바로 RootNavigator가 리마운트되는 등 네비게이션 레이스가 생길 수 있습니다. await signOut() 후에 명시적으로 Login/EmailInput 등으로 reset/replace하는 방식으로 흐름을 고정하는 편이 안전합니다.
    apps/native/src/features/auth/signup/screens/SignupIdentityScreen.tsx:145
  • 주석에 "RootNavigator/StudentNavigator가 step1Completed + isFirstLogin으로 분기"라고 되어 있는데, 현재 분기 로직은 step1Completed/onboardingStatus(및 grade) 기반이고 isFirstLogin은 사용되지 않습니다. 실제 동작과 다른 설명이라 이후 유지보수 시 혼동을 줄 수 있으니 주석을 현재 분기 조건에 맞게 수정해주세요.
    apps/native/src/features/auth/signup/screens/SignupIdentityScreen.tsx:158
  • 뒤로가기 확인에서 useAuthStore.getState().signOut()을 await하지 않고 바로 호출하고 있어, 로그아웃/스토어 reset이 완료되기 전에 네비게이션 컨텍스트가 바뀌면서 레이스/미처리 Promise가 생길 수 있습니다. onPress에서 async 처리로 await signOut()을 수행하고, 이후 목적 화면으로 reset/replace하는 방식으로 흐름을 고정하는 편이 안전합니다.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +60 to +64
reset: () => set(initialState),

setCurrentStep: (step) => set({ currentStep: step }),

setGrade: (grade) => set({ grade }),
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

currentStep을 persist로 저장하지만, 현재 갱신 지점이 setCurrentStep을 호출하는 경우로만 한정되어 있습니다. OnboardingLayout 기본 뒤로가기(navigation.goBack)나 iOS 스와이프-back으로 화면이 바뀌면 currentStep이 동기화되지 않아 앱 종료/재시작 시 잘못된 step으로 복원될 수 있습니다. 화면 focus/route 변경 이벤트에서 currentStep을 업데이트하거나 뒤로가기 handler에서 이전 step을 반영하는 방식으로 동기화를 보완해주세요.

Copilot uses AI. Check for mistakes.
Comment on lines 46 to 49
disabled={isLoading}>
{isLoading && pendingSocial === 'APPLE' ? (
{isLoading ? (
<ActivityIndicator size='small' color='white' />
) : (
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

isLoading이 true이면 이 버튼(그리고 아래의 다른 소셜 버튼들) 모두가 동시에 ActivityIndicator를 렌더링합니다. 실제로는 하나의 provider 로그인만 진행 중인데 여러 버튼이 동시에 로딩처럼 보여 UX 혼란을 유발할 수 있습니다. 현재 시도 중인 provider를 별도 상태로 추적해 해당 버튼에만 스피너를 표시하거나, 공통 로딩 UI로 변경해주세요.

Copilot uses AI. Check for mistakes.
Comment on lines 59 to 62
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Navigator key={activeScreen.key} screenOptions={{ headerShown: false }}>
<Stack.Screen name={activeScreen.name} component={activeScreen.component} />
</Stack.Navigator>
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

여기서 key={activeScreen.key}를 주고 auth-loginauth-signup처럼 key를 바꾸면 AuthNavigator가 리마운트되어 스택이 리셋됩니다. 로그인/회원가입 성공 직후에 Auth 스택 안에서 navigation.navigate('SignupEmail'/'SignupTerms') 등을 호출하는 흐름과 충돌해, 화면이 Login으로 되돌아가거나 언마운트된 navigator로 navigate하는 레이스가 발생할 수 있습니다. key 변경은 Auth↔Student 전환에만 쓰거나, AuthNavigator 내부에서 reset/replace로 목적 화면을 명시하는 방식으로 조정해주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +67
if (data.isFirstLogin) {
// 기존 회원이지만 STEP 1 미완료 → signup flow로
signupStore.setEmail(email);
startOnboarding();
} else {
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

data.isFirstLogin(STEP 1 미완료) 분기에서 signupStore.setEmail/startOnboarding만 수행하고, signup 플로우(약관/본인인증)로의 화면 전환이 없습니다. RootNavigator는 이 상태에서 Auth 스택을 유지하므로 사용자가 다음 단계로 진입하지 못할 수 있습니다. isFirstLogin인 경우 SignupTerms(또는 진행 단계에 맞는 화면)로 navigate/replace/reset을 명시적으로 수행하도록 보완해주세요.

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +58
sessionStatus === 'authenticated' &&
onboardingStatus === 'in-progress' &&
!step1Completed
) {
await useAuthStore.getState().signOut();
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

앱 재시작 시 authenticated + onboardingStatus === 'in-progress' + !step1Completed이면 강제로 signOut()하고 signup/onboarding 스토어도 reset됩니다. PR 설명의 "앱 재시작 시 진행 단계 복원"과 상충하므로, 재시작 후 STEP 1을 이어갈지/로그아웃할지 정책을 정리한 뒤 조건 및 reset 동작(또는 주석)을 맞춰주세요.

Copilot uses AI. Check for mistakes.
- Replace `isLoading` with `loadingProvider` for more granular control
- Use `navigation.reset` for explicit navigation resets in signup flow
- Add `useFocusEffect` to onboarding steps for better state management
- Simplify `RootNavigator` and `AuthNavigator` logic for route handling
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 37 out of 37 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

apps/native/src/features/auth/signup/screens/SignupEmailScreen.tsx:41

  • getEmailExists()는 내부에서 { data }만 반환하고 error를 확인하지 않아(HTTP 오류 시에도 throw되지 않음) dataundefined여도 catch로 가지 않고 회원가입 플로우가 진행될 수 있습니다. data === undefined(또는 error 존재)인 경우를 명시적으로 처리해서 사용자에게 오류를 보여주고 다음 단계로 진행하지 않도록 해 주세요.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +48 to +59
// STEP 1 미완료 상태에서 앱 재시작 시 → 로그아웃 처리 (로그인 화면에서 시작)
const onboardingStatus = useOnboardingStore.getState().status;
const step1Completed = useSignupStore.getState().step1Completed;
const sessionStatus = useAuthStore.getState().sessionStatus;

if (
sessionStatus === 'authenticated' &&
onboardingStatus === 'in-progress' &&
!step1Completed
) {
await useAuthStore.getState().signOut();
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

useLoadAssets에서 authenticated && onboardingStatus === 'in-progress' && !step1Completed인 경우 앱 재시작 시 강제 signOut() 처리하고 있는데, PR 설명(#3)처럼 signupStore를 영속화해 재시작 후 진행단계를 복원하려는 목적 및 RootNavigator/AuthNavigator의 'signup 진행 중이면 Auth 스택 유지' 분기와 동작이 충돌합니다. 재시작 후에도 STEP 1 플로우로 복원하려면 이 강제 로그아웃 로직을 제거하거나, 로그아웃 대신 signup 진행 화면으로 라우팅되도록 처리 방향을 정리해 주세요.

Copilot uses AI. Check for mistakes.
setCurrentStep('MathSubject');
navigation.navigate('MathSubject');
}
}, [grade, navigation, setSelectSubject]);
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

handleNextuseCallback dependency 배열에 setCurrentStep가 포함되어 있지 않습니다. 현재 클로저가 stale해질 수 있고 hook lint에도 걸릴 수 있으니, setCurrentStep를 dependency에 추가하거나(혹은 callback을 useCallback 없이 작성) 의도를 명확히 해 주세요.

Suggested change
}, [grade, navigation, setSelectSubject]);
}, [grade, navigation, setSelectSubject, setCurrentStep]);

Copilot uses AI. Check for mistakes.
setCurrentStep('School');
navigation.navigate('School');
}
}, [grade, selectSubject, navigation, setSchoolId]);
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

handleNextuseCallback dependency 배열에 setCurrentStep가 포함되어 있지 않습니다. 현재는 store action이 안정적일 수 있지만, 훅 규칙/린트 관점에서 누락된 dependency이며 stale 클로저 위험이 있습니다. setCurrentStep를 dependency에 추가해 주세요.

Suggested change
}, [grade, selectSubject, navigation, setSchoolId]);
}, [grade, selectSubject, navigation, setSchoolId, setCurrentStep]);

Copilot uses AI. Check for mistakes.
Ensure `setCurrentStep` is included in the dependency array for
`useCallback`
and improve formatting for `useFocusEffect` across onboarding step
components.
@sterdsterd sterdsterd merged commit 2a986e5 into develop Mar 30, 2026
2 checks passed
@sterdsterd sterdsterd deleted the fix/mat-99-onboarding-flow branch April 7, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants