[SwiftUI][TCA] TCA Case Studies - 02. SharedStateInMemory

별똥별·2025년 1월 31일

TCA

목록 보기
17/24

🚀 TCA에서 Shared 상태 관리하기: InMemoryKey 활용법

안녕하세요, 별똥별🌠입니다!
이번 글에서는 Composable Architecture(TCA)에서 Shared 상태를 관리하는 방법을 살펴보겠습니다. 특히, @Shared 프로퍼티 래퍼를 활용하여 여러 화면에서 데이터를 공유하는 방법과, InMemoryKey를 사용하여 앱이 실행되는 동안만 데이터를 유지하는 방식을 설명합니다. 🔥


🧐 Shared 상태란?

앱을 개발하다 보면 여러 화면에서 동일한 데이터를 유지해야 하는 경우가 있습니다.

예를 들어

  • 여러 탭(Tab)에서 같은 데이터를 유지해야 할 때
  • 특정 화면에서 데이터가 변경되면 다른 화면에서도 즉시 반영되어야 할 때

이러한 문제를 해결하기 위해 TCA에서는 @Shared 프로퍼티 래퍼를 제공합니다. 이를 활용하면 Reducer 간에 상태를 쉽게 공유할 수 있으며, InMemoryKey를 사용하면 앱이 실행되는 동안만 데이터를 유지할 수 있습니다.


🎯 예제: Counter와 Profile에서 Shared State 활용하기

아래 코드는 Counter 탭에서 증가/감소한 숫자 상태를 Profile 탭에서도 확인할 수 있도록 하는 예제입니다. 단, 앱을 종료하면 데이터가 사라지는 방식입니다.

1️⃣ SharedStateInMemory Reducer 정의

@Reducer
struct SharedStateInMemory {
    enum Tab { case counter, profile }
    
    @ObservableState
    struct State: Equatable {
        var currentTab = Tab.counter
        var counter = CounterTab.State()
        var profile = ProfileTab.State()
    }
    
    enum Action: Sendable {
        case counter(CounterTab.Action)
        case profile(ProfileTab.Action)
        case selectTab(Tab)
    }
    
    var body: some ReducerOf<Self> {
        Scope(state: \ .counter, action: \ .counter) {
            CounterTab()
        }
        Scope(state: \ .profile, action: \ .profile) {
            ProfileTab()
        }
        
        Reduce { state, action in
            switch action {
            case .counter, .profile:
                return .none
            case let .selectTab(tab):
                state.currentTab = tab
                return .none
            }
        }
    }
}

이 Reducer는 두 개의 서브 Reducer(CounterTab과 ProfileTab)을 포함하며, 탭 간 상태 전환을 관리합니다.


2️⃣ CounterTab에서 @Shared 상태 활용하기

@Reducer
struct CounterTab {
    @ObservableState
    struct State: Equatable {
        @Presents var alert: AlertState<Action.Alert>?
        @Shared(.stats) var stats = Stats()
    }
    
    enum Action: Sendable {
        case alert(PresentationAction<Alert>)
        case decrementButtonTapped
        case incrementButtonTapped
        case isPrimeButtonTapped
        
        enum Alert: Equatable {}
    }
    
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .alert:
                return .none
            
            case .decrementButtonTapped:
                state.$stats.withLock { $0.decrement() }
                return .none
                
            case .incrementButtonTapped:
                state.$stats.withLock { $0.increment() }
                return .none
                
            case .isPrimeButtonTapped:
                state.alert = AlertState {
                    TextState(
                        isPrime(state.stats.count)
                        ? "👍 The number \(state.stats.count) is prime!"
                        : "👎 The number \(state.stats.count) is not prime :("
                    )
                }
                return .none
            }
        }
        .ifLet(\ .$alert, action: \ .alert)
    }
}

@Shared(.stats)를 사용하여 Stats 객체를 공유합니다. incrementButtonTappeddecrementButtonTapped 액션이 호출되면, 공유된 Stats 인스턴스가 변경되며 ProfileTab에서도 즉시 반영됩니다.


3️⃣ ProfileTab에서 공유된 상태 활용하기

@Reducer
struct ProfileTab {
    @ObservableState
    struct State: Equatable {
        @Shared(.stats) var stats = Stats()
    }
    
    enum Action: Sendable {
        case resetStatsButtonTapped
    }
    
    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .resetStatsButtonTapped:
                state.$stats.withLock { $0 = Stats() }
                return .none
            }
        }
    }
}

ProfileTab에서도 @Shared(.stats)를 사용하여 CounterTab과 동일한 Stats 상태를 공유합니다. 이로 인해 CounterTab에서 증가/감소된 값이 ProfileTab에서도 유지되며, Reset 버튼을 누르면 상태가 초기화됩니다.


✅ InMemoryKey vs. FileStorageKey

비교 항목InMemoryKeyFileStorage
데이터 유지 범위앱 실행중앱이 종료된 후에도 유지
저장 위치메모리파일 (Documents, UserDefaults 등)
활용 사례화면 간 상태 공유영구적인 설정, 사용자 데이터 저장

위에서 사용한 InMemoryKey는 앱이 실행되는 동안에만 상태를 유지하므로, 앱을 종료하면 Stats 데이터가 초기화됩니다. 반면, FileStorageKey를 사용하면 Stats 데이터를 파일로 저장하여 앱이 종료된 후에도 값을 유지할 수 있습니다.


✅ 정리

  • @Shared를 사용하면 여러 Reducer에서 상태를 공유할 수 있다.
  • InMemoryKey는 앱이 실행되는 동안에만 데이터를 유지한다.
  • FileStorageKey를 활용하면 앱이 종료되어도 데이터를 유지할 수 있다.
  • @Shared(.stats)를 사용하여 CounterTabProfileTab에서 동일한 데이터를 사용할 수 있다.

이제 여러분도 TCA에서 Shared 상태를 활용하여 강력한 상태 관리를 구현해보세요! 🚀🔥
다음 글에서 더 흥미로운 TCA 활용 사례를 다뤄보겠습니다. 감사합니다! 😊

profile
밍밍

0개의 댓글