[SwiftUI][TCA] TCA Case Studies - 01. Binding basics

별똥별·2025년 1월 21일

TCA

목록 보기
10/24

안녕하세요. 별똥별🌠입니다.
지난 시간에는 TCA Case Studies의 또 다른 사례를 통해 Composable Architecture를 학습했었죠. 이번 시간에는 양방향 바인딩(Bidirectional Binding)을 활용하여 단방향 데이터 흐름을 유지하면서도 SwiftUI의 강력한 바인딩 기능을 사용하는 방법을 소개하겠습니다.


양방향 바인딩 in TCA

SwiftUI는 TextField, Toggle, Slider 등 여러 UI 요소에서 양방향 바인딩을 제공합니다. 하지만, TCA는 모든 상태 변경이 Reducer를 통해 이루어지는 단방향 데이터 흐름을 기반으로 합니다. 이 둘을 결합하려면 BindableState와 sending 메서드를 활용해 상태를 안전하게 관리해야 합니다.


1️⃣ Reducer 정의

BindingBasics Reducer는 UI 요소(TextField, Toggle, Stepper, Slider) 각각에 필요한 상태와 액션을 정의하고, 상태 변경 로직을 처리합니다.

@Reducer
struct BindingBasics {
    @ObservableState
    struct State: Equatable {
        var sliderValue = 5.0
        var stepCount = 10
        var text = ""
        var toggleIsOn = false
    }
    
    enum Action {
        case sliderValueChanged(Double)
        case stepCountChanged(Int)
        case textChanged(String)
        case toggleChanged(isOn: Bool)
    }
    
    var body: some Reducer<State, Action> {
        Reduce { state, action in
            switch action {
            case let .sliderValueChanged(value):
                state.sliderValue = value
                return .none
                
            case let .stepCountChanged(count):
                state.sliderValue = .minimum(state.sliderValue, Double(count))
                state.stepCount = count
                return .none
                
            case let .textChanged(text):
                state.text = text
                return .none
                
            case let .toggleChanged(isOn: isOn):
                state.toggleIsOn = isOn
                return .none
            }
        }
    }
}

💡 주요 포인트

  • State: UI의 각 요소(TextField, Slider 등)에 필요한 값을 정의합니다.
  • Action: 사용자의 입력 이벤트를 처리하기 위한 액션을 정의합니다.
  • Reducer: 상태 변경 로직을 처리하며, 예를 들어 stepCount가 변경되면 sliderValue가 stepCount를 초과하지 않도록 제약을 추가합니다.

2️⃣ View 구현

BindingBasicsView는 @Bindable 스토어를 사용해 UI와 상태를 연결합니다. 이를 통해 SwiftUI의 양방향 바인딩을 단방향 데이터 흐름 안에 녹여낼 수 있습니다.

struct BindingBasicsView: View {
    @Bindable var store: StoreOf<BindingBasics>
    
    var body: some View {
        Form {
            Section {
                AboutView(readMe: readMe)
            }
            
            // TextField
            HStack {
                TextField("Type here", text: $store.text.sending(\.textChanged))
                    .disableAutocorrection(true)
                    .foregroundStyle(store.toggleIsOn ? .secondary : .primary)
                Text(alternate(store.text))
            }
            .disabled(store.toggleIsOn)
            
            // Toggle
            Toggle("Disable other controls", isOn: $store.toggleIsOn.sending(\.toggleChanged))
            
            // Stepper
            Stepper("Max slider value: \(store.stepCount)", value: $store.stepCount.sending(\.stepCountChanged), in: 0...100)
                .disabled(store.toggleIsOn)
            
            // Slider
            HStack {
                Text("Slider value: \(Int(store.sliderValue))")
                Slider(value: $store.sliderValue.sending(\.sliderValueChanged), in: 0...Double(store.stepCount))
                    .tint(.accentColor)
            }
            .disabled(store.toggleIsOn)
        }
        .monospacedDigit()
        .navigationTitle("Bindings basics")
    }
}

💡 주요 포인트

  • TextField:
    text 상태를 @Bindable로 연결하며, 입력값 변화는 .textChanged 액션으로 처리됩니다.
  • Toggle:
    다른 컨트롤을 활성화/비활성화하는 로직을 toggleIsOn 상태로 관리합니다.
  • Stepper와 Slider:
    stepCount는 Slider의 최대값으로 동작하며, Slider는 상태를 변경하면서 stepCount를 초과하지 않도록 제한합니다.

3️⃣ Preview를 활용한 테스트

Preview를 통해 UI와 상태 변경을 빠르게 테스트합니다.

#Preview {
    NavigationStack {
        BindingBasicsView(store: .init(initialState: BindingBasics.State()) {
            BindingBasics()
        })
    }
}


🌟 핵심 포인트 정리

  1. TCA에서 양방향 바인딩 처리
    @Bindable과 sending 메서드를 통해 SwiftUI의 양방향 바인딩과 TCA의 단방향 데이터 흐름을 통합할 수 있습니다.
    모든 상태 변경은 Reducer를 통해 이루어지므로 상태 관리의 예측 가능성과 유지보수성을 높일 수 있습니다.
  1. 상태와 액션의 연결
    상태 변경 로직은 Reducer에서 처리하며, 상태와 액션을 명확히 분리합니다.
    UI 요소의 값 변화는 Action으로 전송되고, Reducer에서 이를 처리한 후 다시 View에 반영됩니다.
  1. 효율적인 상태 관리
    toggleIsOn 상태를 통해 다른 컨트롤(TextField, Slider 등)을 활성화/비활성화하는 로직을 간결하게 구현할 수 있습니다.

마무리

이 예제는 TCA에서 SwiftUI의 강력한 바인딩 기능을 어떻게 단방향 데이터 흐름에 적합하게 구현할 수 있는지를 잘 보여줍니다. 이번 포스팅을 통해 TCA의 활용 방법을 더욱 깊이 이해하길 바랍니다! 😊

profile
밍밍

0개의 댓글