iOS Development Guide

상태 복원과 전역 상태 동기화 설계

화면 전환 중 복구와 전역 상태 반영이 겹치면 충돌이 생긴다. 해결 포인트는 순서를 고정하고, 복원 범위와 전역 갱신 범위를 분리하는 것이다.

State restoration Global state sync Knowledge item #24
학습 날짜

기록 없음

권장 처리 순서

단계 처리 주의점
1 입력 이벤트 정규화(복귀/딥링크/푸시를 공통 Intent로 변환) 수신 위치별 분기 코드가 바로 화면 전환하지 않도록 차단
2 전역 상태 선반영(인증/권한/실험군/필수 데이터) 상태 확정 전에 라우팅하면 잘못된 화면 진입 가능
3 scene별 복원 상태 점검(복원 가능 여부, 스택 유효성) 복원 스택이 오래된 세션 기준이면 폐기 조건 필요
4 라우팅 실행(활성 scene 우선, 비활성 scene은 대기열 보관) 동시에 여러 전환을 실행하지 않고 직렬 처리

경계 분리 규칙

  • App 레벨: 전역 상태 확정, 이벤트 우선순위 정책 관리
  • Scene 레벨: 네비게이션 스택 복원, 실제 화면 전환 실행
  • Feature 레벨: 화면별 파라미터 해석, 유효성 검증
  • 전역 상태와 UI 상태를 한 store에 섞지 않고 읽기 경계 분리

우선순위 예시

  • 1순위: 인증 강제(세션 만료, 약관 재동의)
  • 2순위: 보안/권한 필수 화면
  • 3순위: 푸시/딥링크 목적 화면
  • 4순위: 마지막 복원 화면
우선순위는 제품 정책에 맞게 고정하고, 실행 로그로 추적 가능해야 한다.

충돌 방지 구현 패턴

enum AppIntent {
  case resume
  case deepLink(URL)
  case push([AnyHashable: Any])
}

@MainActor
final class NavigationCoordinator {
  private var queue: [AppIntent] = []
  private var isRouting = false

  func enqueue(_ intent: AppIntent) {
    queue.append(intent)
    drainIfNeeded()
  }

  private func drainIfNeeded() {
    guard isRouting == false, queue.isEmpty == false else { return }
    isRouting = true

    Task {
      let intent = queue.removeFirst()
      await GlobalStateStore.shared.refreshIfNeeded()
      await SceneRestorationService.shared.validateStack()
      await route(intent)
      isRouting = false
      drainIfNeeded()
    }
  }

  private func route(_ intent: AppIntent) async {
    // scene 활성 여부 및 정책에 따라 라우팅
  }
}
핵심은 "정규화 -> 전역 상태 확정 -> 복원 검증 -> 라우팅" 순서를 강제하는 것이다.

Q

관련 문서: iOS 개발 상식 100선