배경
현재 앱 초기 라우팅은 서버의 /member/profile.onboarding_completed가 아니라 로컬 MMKV/Zustand의 hasCompletedOnboarding 값을 보고 결정된다.
그 결과 서버 profile 값은 onboarding_completed: false인데, 로컬 MMKV의 onboarding.completed가 true로 남아 있으면 앱이 온보딩이 아니라 /(tabs)로 이동하는 불일치가 발생한다.
이번 작업은 버그 수정 + 상태 저장 경계 정립을 단일 PR로 처리한다.
문제
1. 온보딩 완료 여부 source가 분산됨
| Source |
현재 용도 |
문제 |
MMKV onboarding.completed |
앱 시작 라우팅 |
서버 truth와 불일치 가능 |
로그인 응답 onboarding_completed |
로그인 직후 onboarding store 복사 |
재시작/새로고침 시 반영 안 됨 |
/member/profile.onboarding_completed |
settings 표시 |
bootstrap 라우팅 미사용 |
온보딩 완료 여부는 서버 데이터이므로 로컬 MMKV가 source of truth가 되면 안 된다.
2. 상태 저장 4계층이 코드·문서에 명시되지 않음
아래 원칙이 팀 합의 수준으로 문서화되어 있지 않아, 구현 시 계층이 섞이기 쉽다.
| 계층 |
저장소 |
역할 |
| 인증 상태 |
SecureStore |
token, deviceId 등 민감 credential |
| 서버 데이터 |
TanStack Query |
profile, schedule 등 API 응답 cache |
| 클라이언트 전역 상태 |
Zustand |
세션 런타임 미러, wizard draft 등 휘발 UI state |
| 비민감 로컬 persistence |
MMKV |
오프라인 draft, 최근 검색 등 서버 truth가 아닌 값 |
목표
- 온보딩 완료 여부의 source of truth를
/member/profile.onboarding_completed로 통일한다.
- 앱 초기 / 로그인 직후 / 온보딩 완료 직후 라우팅이 모두 같은 기준을 사용하도록 한다.
- 상태 저장 4계층 원칙을 docs에 반영하고, 전체 코드베이스를 점검해 위반 사항을 이번 PR에서 정리한다.
작업 범위
A. Onboarding completion source of truth 수정
B. 상태 저장 4계층 문서화
C. 전체 코드베이스 점검 (이번 PR에서 반영)
현재 파악된 저장소 사용 현황을 기준으로 위반·과도기 항목을 점검하고, 수정 가능한 것은 이번 PR에 포함한다.
| 영역 |
파일/키 |
현재 |
기대 |
| Auth |
token-storage → SecureStore |
OK |
유지 |
| Auth |
device-id → SecureStore |
OK |
유지 |
| Auth |
useAuthStore |
런타임 미러 |
유지 (credential persist 금지) |
| Onboarding |
onboarding.completed MMKV |
서버 truth 캐시 |
제거 |
| Onboarding |
useOnboardingStore.preferences |
Zustand 메모리 |
유지 |
| Member |
useMemberProfileQuery |
settings만 사용 |
bootstrap 라우팅에도 사용 |
| Schedule |
card-store MMKV |
로컬 카드/검색 persist |
과도기 유지, 문서에 “서버 전환 전 interim” 명시 |
| Constants |
USER_PREFERENCES |
미사용 |
사용처 없으면 문서/TODO 정리 |
점검 시 추가로 확인할 항목:
비범위
- settings UI 수정
- schedule/card-store MMKV 구조 변경 (문서화만)
- 전체 Zustand store 대수술
- Orval generated 파일 수정
- E2E 테스트 추가 (별도 이슈)
구현 메모 (단일 PR)
권장 흐름:
hydrateSession (SecureStore)
→ isAuthenticated?
→ No: /login
→ Yes: fetch member profile (TanStack Query)
→ profile.hasCompletedOnboarding?
→ false: /onboarding
→ true: /(tabs)
공통 라우팅 헬퍼 또는 hook 추출을 검토해 index.tsx, login-screen.tsx, transport-screen.tsx가 같은 기준을 쓰게 한다.
검증
완료 조건
- 온보딩 완료 여부는 더 이상 로컬 MMKV가 앱 라우팅의 source of truth가 아니다.
- 앱 초기 / 로그인 직후 / 온보딩 완료 직후 라우팅이 member profile 기준으로 일관된다.
- 서버 profile과 로컬 MMKV가 불일치해도 서버 profile이 우선한다.
- 상태 저장 4계층 원칙이 docs에 반영되고 AGENTS.md에서 참조 가능하다.
- 코드베이스 점검 결과가 문서 또는 PR 설명에 남아 있다.
관련 파일 (예상)
src/app/index.tsx
src/app/_layout.tsx
src/screens/auth/login-screen.tsx
src/screens/onboarding/transport-screen.tsx
src/domains/onboarding/use-onboarding-store.ts
src/domains/member/api/queries.ts
src/domains/auth/model.ts
src/domains/auth/api/mapper.ts
docs/architecture/state-storage.md (신규)
AGENTS.md
배경
현재 앱 초기 라우팅은 서버의
/member/profile.onboarding_completed가 아니라 로컬 MMKV/Zustand의hasCompletedOnboarding값을 보고 결정된다.그 결과 서버 profile 값은
onboarding_completed: false인데, 로컬 MMKV의onboarding.completed가true로 남아 있으면 앱이 온보딩이 아니라/(tabs)로 이동하는 불일치가 발생한다.이번 작업은 버그 수정 + 상태 저장 경계 정립을 단일 PR로 처리한다.
문제
1. 온보딩 완료 여부 source가 분산됨
onboarding.completedonboarding_completed/member/profile.onboarding_completed온보딩 완료 여부는 서버 데이터이므로 로컬 MMKV가 source of truth가 되면 안 된다.
2. 상태 저장 4계층이 코드·문서에 명시되지 않음
아래 원칙이 팀 합의 수준으로 문서화되어 있지 않아, 구현 시 계층이 섞이기 쉽다.
목표
/member/profile.onboarding_completed로 통일한다.작업 범위
A. Onboarding completion source of truth 수정
profile.hasCompletedOnboarding기준으로 라우팅한다.src/app/index.tsx가useOnboardingStore.hasCompletedOnboarding또는 MMKV 값만 보고 라우팅하지 않도록 수정한다.src/app/_layout.tsx의hydrateOnboarding()호출을 제거하거나 bootstrap 책임에서 분리한다.setOnboardingCompleted(session.hasCompletedOnboarding)에 의존하지 않도록 수정한다.completeOnboarding()이 API 성공 전에 local completed 값을true로 persist하지 않도록 수정한다.onboardingCompleted값을 기준으로 라우팅한다.preferencesdraft 상태는 Zustand 메모리로 유지한다.onboarding.completedMMKV key,hasCompletedOnboarding,hydrateOnboarding,setOnboardingCompletedlegacy 사용 경로를 제거한다.AuthSession.hasCompletedOnboarding이 bootstrap 라우팅에 쓰이지 않도록 정리한다 (필요 시 mapper/model에서 역할 축소).B. 상태 저장 4계층 문서화
docs/architecture/에 상태 저장 경계 문서를 추가한다 (예:state-storage.md).AGENTS.md와docs/conventions/core.md(또는 overview)에서 위 문서를 링크한다.dependency-rules.md/api-boundary.md와 충돌 없이 보완 관계로 정리한다.C. 전체 코드베이스 점검 (이번 PR에서 반영)
현재 파악된 저장소 사용 현황을 기준으로 위반·과도기 항목을 점검하고, 수정 가능한 것은 이번 PR에 포함한다.
token-storage→ SecureStoredevice-id→ SecureStoreuseAuthStoreonboarding.completedMMKVuseOnboardingStore.preferencesuseMemberProfileQuerycard-storeMMKVUSER_PREFERENCES점검 시 추가로 확인할 항목:
index.tsx,login-screen.tsx,transport-screen.tsx) 라우팅 기준 일관성비범위
구현 메모 (단일 PR)
권장 흐름:
공통 라우팅 헬퍼 또는 hook 추출을 검토해
index.tsx,login-screen.tsx,transport-screen.tsx가 같은 기준을 쓰게 한다.검증
/member/profile.onboarding_completed=false→ 온보딩 이동/member/profile.onboarding_completed=true→/(tabs)이동onboarding.completed=true가 남아 있어도 profile이 false면 온보딩 이동/(tabs)로 이동하지 않음/(tabs)이동npm run type-checknpm run lint완료 조건
관련 파일 (예상)
src/app/index.tsxsrc/app/_layout.tsxsrc/screens/auth/login-screen.tsxsrc/screens/onboarding/transport-screen.tsxsrc/domains/onboarding/use-onboarding-store.tssrc/domains/member/api/queries.tssrc/domains/auth/model.tssrc/domains/auth/api/mapper.tsdocs/architecture/state-storage.md(신규)AGENTS.md