TCA의 기본 개념
TCA는 현재 상태가 어떤지 파악하고 이를 관리하기 쉽게 하기 위해 고안된 단방향 아키텍처이다. 여기서 단방향 아키텍처라는 부분에 집중해보자.
먼저 MVVM패턴에 대해서 알아보면 MVVM 또한 단반향 아키텍처로서 View가 ViewModel의 상태를 관찰하고 해당 상태에 따라 업데이트 된다. View는 자기 자신을 렌더링하고 유지하기 위해 ViewModel에게 자신의 상태와 로직을 전달한다.
따라서 View는 그 상태와 로직이 어떤 방식으로 변형되는지 알지 못해야 한다. 그러나 SwiftUI와 같은 양방향 데이터 바인딩을 지원하는 프레임워크를 사용하면 View가 ViewModel에서의 요청을 받아와서 추가적인 작업을 해야하는 경우가 발생한다. 이를 방지하기 위해 @Published 속성들에 대한 엄격한 캡슐화를 지향하는 사람들도 있으나, 이에 대한 일관적인 가이드 라인은 없었다.. 결국엔 나중에 언제 어디서 상태가 바뀌는지 파악하기 어려운 경우도 생긴다는 것이다.
이와 달리 TCA는 상태 변화에 대한 일관적인 가이드라인을 컴파일 단계에서부터 확보한다. 어떤 개발자든 TCA를 적용한다면 상태 변화를 일으키는 Action 정의, Action에 대한 View에서의 .send() 를 거쳐야 하는 것이다!
TCA는 원래의 목적을 달성하기 위해 Single Source Of Truth를 따르는 철저한 단방향 구조를 선택했다. 앱의 상태나 데이터에 대한 유일한 출처를 가지는 것을 의미한다. 이렇게 될 경우, 모든 상태 변경의 출처가 명확해지고 그 변경이 예측, 추적이 가능해진다.
TCA에서는 변동을 추적하고 의도하지 않는 변경에서 지켜내야할 상태를 State라고 한다. 사용자가 View를 통해 어떠한 작업이나 알림, 이벤트를 트리거 하면 연결된 Action이 Reducer 내 구현된 함수로 State를 변화시키는 Effect를 반환 한다. 이때, 그 작업이 크게 복잡하지 않다면 바로 상태를 변화시킬 수 있고 때로는 네트워크 통신과 같은 비동기적인 작업이 필요한 경우, 통신을 실행하는 데 필요한 환경과 자원이 필요하고 이를 ‘의존성을 갖게 된다’라고 표현한다.
이렇게 도출된 Effect에 따라 다시 Action을 선택해 State를 변경한다. 이렇게 변경된 State는 다시 뷰에 영향을 주게 되는 흐름으로 TCA는 작동한다.
State
State는 Reducer의 현재 상태를 갖는 구조체 이다. 비즈니스 로직을 수행하거나 UI를 그릴 때 필요한 데이터에 대한 설명을 나타낼때 사용한다.
예를 들어 버튼을 눌러 Count 갯수를 늘리는 작업을 진행한다고 했을 때 이렇게 할 수 있다.
import ComposableArchitecture
struct CounterFeature: Reducer {
struct State: Equatable {
var count = 0
}
enum Action: Equatable {
case increment
case decrement
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .increment:
state.count += 1
return .none
case .decrement:
state.count -= 1
return .none
}
}
}
}
먼저 Feature를 구현해서 값을 전달할 수 있는 객체를 만들어보자 State를 활용해 count 값을 저장하는 형태를 정의하고,
Action을 통해 열거형으로 두가지 액션을 정의해 상태 변경을 유발할 수 있는 동작들을 나열한다.
그 이후 Reducer는 State와 Action을 받아 업데이트를 하는 함수로 Reduce를 활용해 count를 증가시키거나 감소시키는 로직을 구현한다.
이때 ReduceOf<Self>를 사용하여 리듀서가 본질적으로 자신을 사용해 동작하도록 설정해주었다.
그 다음 구현한 Feature를 활용해 View에 연결하는 과정을 구현해보면
import ComposableArchitecture
struct CounterView: View {
let store: StoreOf<CounterFeature>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
VStack {
Text("점수 : \\(viewStore.count)")
.font(.largeTitle)
HStack {
Button("-1") {
viewStore.send(.decrement)
}
.padding()
Button("+1") {
viewStore.send(.increment)
}
.padding()
}
}
}
}
}
store는 TCA에서 state와 action을 관리하는 핵심이다. 이 store는 CounterFeature의 상태와 액션을 다루기 때문에 여기서 카운터의 값을 증가/감소하는 기능을 제공한다.
WithViewStore는 뷰에서 store의 상태를 관찰하고 상태 변화가 있을 때 UI를 자동으로 업데이트하는 역할을 한다. 또한 액션을 처리하는 send 메서드를 통해 사용자가 상호작용할 수 있도록 연결해 준다.
viewStore.send() 를 통해 .increment, .decrement를 호출하여 CounterFeature에 정의된 액션을 실행하고 이에 따라 count 값을 변경한다.
이렇게 오늘은 간단하게 state와 action을 활용해 Feature를 구성하고 구성한 Feature를 활용해 View에 연결하는 것을 구현해보았다.
다음엔 TCA에 대해 더 자세하게 알아보는 시간을 가져보자!
오늘은 여기까지!!
'◽️ Programming > TCA' 카테고리의 다른 글
SwiftUI TCA - Dependency, Reducer, Effect에 대해서 알아보자!! (0) | 2024.11.21 |
---|---|
Share Run TCA 로직 구현 (3) | 2024.09.30 |
SwiftUI TCA (3) - Binding (0) | 2024.09.23 |
SwiftUI TCA (2) - Effect (0) | 2024.09.15 |