Skip to content

fix: member profile을 onboarding 완료 source of truth로 통일 + 상태 저장 4계층 정리 #59

Description

@haewonwon

배경

현재 앱 초기 라우팅은 서버의 /member/profile.onboarding_completed가 아니라 로컬 MMKV/Zustand의 hasCompletedOnboarding 값을 보고 결정된다.

그 결과 서버 profile 값은 onboarding_completed: false인데, 로컬 MMKV의 onboarding.completedtrue로 남아 있으면 앱이 온보딩이 아니라 /(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가 아닌 값

목표

  1. 온보딩 완료 여부의 source of truth를 /member/profile.onboarding_completed로 통일한다.
  2. 앱 초기 / 로그인 직후 / 온보딩 완료 직후 라우팅이 모두 같은 기준을 사용하도록 한다.
  3. 상태 저장 4계층 원칙을 docs에 반영하고, 전체 코드베이스를 점검해 위반 사항을 이번 PR에서 정리한다.

작업 범위

A. Onboarding completion source of truth 수정

  • 앱 시작 시 SecureStore session hydrate 후 인증 여부를 확인한다.
  • 인증된 사용자는 member profile을 조회한 뒤 profile.hasCompletedOnboarding 기준으로 라우팅한다.
  • src/app/index.tsxuseOnboardingStore.hasCompletedOnboarding 또는 MMKV 값만 보고 라우팅하지 않도록 수정한다.
  • src/app/_layout.tsxhydrateOnboarding() 호출을 제거하거나 bootstrap 책임에서 분리한다.
  • 로그인 성공 직후 setOnboardingCompleted(session.hasCompletedOnboarding)에 의존하지 않도록 수정한다.
  • 로그인 직후에도 member profile / server truth 기준으로 라우팅한다.
  • completeOnboarding()이 API 성공 전에 local completed 값을 true로 persist하지 않도록 수정한다.
  • 온보딩 완료 mutation 성공 후 member profile을 invalidate/refetch하고 최신 onboardingCompleted 값을 기준으로 라우팅한다.
  • 기존 onboarding wizard preferences draft 상태는 Zustand 메모리로 유지한다.
  • onboarding.completed MMKV key, hasCompletedOnboarding, hydrateOnboarding, setOnboardingCompleted legacy 사용 경로를 제거한다.
  • AuthSession.hasCompletedOnboarding이 bootstrap 라우팅에 쓰이지 않도록 정리한다 (필요 시 mapper/model에서 역할 축소).

B. 상태 저장 4계층 문서화

  • docs/architecture/에 상태 저장 경계 문서를 추가한다 (예: state-storage.md).
    • 4계층 정의 (SecureStore / TanStack Query / Zustand / MMKV)
    • 각 계층의 허용·금지 예시
    • Auth bootstrap / Onboarding wizard / Server state 경계 다이어그램
    • “서버 truth를 MMKV에 캐시하지 않는다” 원칙 명시
  • AGENTS.mddocs/conventions/core.md(또는 overview)에서 위 문서를 링크한다.
  • 기존 dependency-rules.md / api-boundary.md와 충돌 없이 보완 관계로 정리한다.

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 정리

점검 시 추가로 확인할 항목:

  • screen/component에서 generated API 직접 import 여부 (기존 boundary 준수)
  • Zustand store가 서버 응답을 persist하는 패턴 여부
  • login/auth mapper가 서버 필드를 로컬 store에 복사하는 anti-pattern 여부
  • bootstrap 진입점(index.tsx, login-screen.tsx, transport-screen.tsx) 라우팅 기준 일관성

비범위

  • 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가 같은 기준을 쓰게 한다.


검증

  • fresh install 또는 앱 데이터 초기화 후 로그인 시 정상 라우팅
  • /member/profile.onboarding_completed=false → 온보딩 이동
  • /member/profile.onboarding_completed=true/(tabs) 이동
  • MMKV onboarding.completed=true가 남아 있어도 profile이 false면 온보딩 이동
  • 온보딩 API 실패 시 /(tabs)로 이동하지 않음
  • 온보딩 API 성공 후 profile refetch 결과가 true일 때만 /(tabs) 이동
  • 로그아웃 후 재로그인해도 profile 기준 라우팅
  • npm run type-check
  • npm run lint

완료 조건

  • 온보딩 완료 여부는 더 이상 로컬 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions