[SwiftUI][TCA] TCA Case Studies - 01. BindingForm

별똥별·2025년 1월 22일

TCA

목록 보기
11/24
post-thumbnail

안녕하세요, 별똥별🌠입니다.

지난 시간에는 TCA Case Studies의 또 다른 사례를 통해 Composable Architecture를 학습했었죠. 이번 시간에는 양방향 바인딩(Bidirectional Binding)을 활용하면서, 상태를 초기화할 수 있는 리셋 기능을 추가한 예제를 소개하겠습니다.
TCA와 SwiftUI를 함께 사용하여 상태와 UI 간 연결을 매끄럽게 처리하는 방법을 살펴봅시다! 🚀


양방향 바인딩 in TCA

SwiftUI는 TextField, Toggle, Slider와 같은 UI 요소에서 양방향 바인딩을 제공합니다. 반면, TCA는 단방향 데이터 흐름을 기반으로 모든 상태 변경을 Reducer를 통해 관리합니다.
이 둘을 결합하려면 @BindableState와 BindingReducer를 활용해 상태와 액션을 관리해야 합니다.


1️⃣ Reducer 정의

아래는 리셋 버튼으로 상태를 초기화할 수 있는 기능이 추가된 Reducer 예제입니다.

@Reducer
struct BindingForm {
    @ObservableState
    struct State: Equatable {
        var sliderValue = 5.0
        var stepCount = 10
        var text = ""
        var toggleIsOn = false
    }
    
    enum Action: BindableAction {
        case binding(BindingAction<State>) // 바인딩 액션
        case resetButtonTapped // 리셋 버튼 액션
    }
    
    var body: some Reducer<State, Action> {
        BindingReducer() // @Bindable로 바인딩 처리
        Reduce { state, action in
            switch action {
            case .binding(\.stepCount):
                state.sliderValue = .minimum(state.sliderValue, Double(state.stepCount))
                return .none
                
            case .binding:
                return .none
                
            case .resetButtonTapped:
                state = State() // 상태 초기화
                return .none
            }
        }
    }
}

💡 주요 포인트

  • State
    • UI에서 사용할 sliderValue, stepCount, text, toggleIsOn 등의 상태를 정의합니다.

  • Action
    • binding : View와 State 간 양방향 바인딩 처리를 위한 액션입니다.
    • resetButtonTapped : 리셋 버튼이 눌렸을 때 상태를 초기화하는 액션입니다.

  • BindingReducer
    • @Bindable을 활용해 View와 State 간 바인딩을 처리합니다.

  • Reduce 로직
    • stepCount 변경 시 sliderValue가 초과되지 않도록 제약을 설정합니다.
    • 리셋 버튼을 누르면 상태를 초기값으로 초기화합니다.


2️⃣ View 구현

View에서는 @Bindable을 활용해 Store와 UI 간 상태를 연결합니다. 이를 통해 양방향 바인딩을 단방향 데이터 흐름 안에서 구현합니다.

struct BindingFormView: View {
    @Bindable var store: StoreOf<BindingForm>
    
    var body: some View {
        Form {
            Section {
                AboutView(readMe: readMe)
            }
            
            // TextField와 Toggle
            HStack {
                TextField("Type here", text: $store.text)
                    .disableAutocorrection(true)
                    .foregroundStyle(store.toggleIsOn ? .secondary : .primary)
                Text(alternate(store.text))
            }
            .disabled(store.toggleIsOn)
            
            Toggle("Disable other controls", isOn: $store.toggleIsOn.resignFirstResponder())
            
            // Stepper와 Slider
            Stepper("Max slider value: \(store.stepCount)", value: $store.stepCount, in: 0...100)
                .disabled(store.toggleIsOn)
            
            HStack {
                Text("Slider value: \(Int(store.sliderValue))")
                Slider(value: $store.sliderValue, in: 0...Double(store.stepCount))
                    .tint(.accentColor)
            }
            .disabled(store.toggleIsOn)
            
            // Reset 버튼
            Button("Reset") {
                store.send(.resetButtonTapped)
            }
            .tint(.red)
        }
        .monospaced()
        .navigationTitle("Bindings form")
    }
}

💡 주요 포인트

  • TextField와 Toggle
    • store.text와 바인딩해 사용자의 입력값을 관리하며, Toggle 상태에 따라 UI를 비활성화합니다.

  • Stepper와 Slider
    • stepCount는 Slider의 최대값 역할을 하며, Stepper로 값 변경 시 Slider 값이 초과하지 않도록 조정됩니다.

  • Reset 버튼
    • 리셋 버튼을 누르면 .resetButtonTapped 액션이 호출되어 상태가 초기화됩니다.

3️⃣ Preview로 테스트하기

Preview를 활용해 UI와 상태 변경 동작을 빠르게 테스트할 수 있습니다.

#Preview {
    NavigationStack {
        BindingFormView(store: .init(initialState: BindingForm.State(), reducer: {
            BindingForm()
        }))
    }
}

4️⃣ 기존 코드와의 차이점 비교

이전 예제에서는 개별 액션(sliderValueChanged, textChanged 등)을 정의해 상태 변경을 처리했습니다.
이번 예제에서는 TCA의 BindableState와 BindingReducer를 활용해, 상태 변경 로직을 간결하게 작성할 수 있었습니다.

Binding Basic 코드Binding Form 코드
Action에 개별 변경 BindingAction 으로 바인딩 처리 통함
상태 변경 로직을 세분화하여 작성BindingReducer 로 기본 로직 간소화
단일 상태 변경을 위한 여러 액션 관리 필요.binding 을 사용해 액션 관리 최소화

장점

  • 간결성: 기존 코드보다 액션과 상태 변경 로직이 단순해졌습니다.
  • 유지보수성: 새로운 UI 요소가 추가되어도 @Bindable과 BindingReducer를 활용하면 쉽게 확장할 수 있습니다.

🌟 핵심 포인트 정리

  • TCA의 양방향 바인딩
    • @Bindable과 BindingReducer를 통해 상태와 UI 간 연결을 간소화했습니다.

  • 리셋 기능 추가
    • 리셋 버튼을 눌러 상태를 초기화하는 방법을 배웠습니다.

  • 상태 관리 효율성
    • 기존 방식보다 간결하고 효율적으로 상태를 관리할 수 있었습니다.

이번 포스팅을 통해 TCA와 SwiftUI를 결합해 효율적인 상태 관리를 구현하는 방법을 익히셨길 바랍니다.
다음에도 더 흥미롭고 유익한 내용으로 찾아뵙겠습니다! 😊
감사합니다!

profile
밍밍

0개의 댓글