Search
Duplicate
🛼

변경을 처리한다: Reducer

Created
2024/02/17 14:59
Tags

 변경을 처리한다: Reducer

Reducer 는 애플리케이션의 현재 State 를 주어진 Action 을 바탕으로 어떻게 다음 State 로 바꿀 것이지를 묘사하고,
어떤 결과(Effect )가 존재한다면 Store 를 통해 어떻게 실행되어야 하는지를 설명하는 프로토콜이다.
애플리케이션의 현재 상태(State )를 함수형으로, 가독성이 좋게 작성하기 위해 고안된 개념이다.
→ 클라이언트의 입장에서 사용자의 상호작용에 따라 상태를 변형할 수 있도록 돕는다
TCA에서 사용자가 뷰에서 Action 을 취하면 ActionReducer 를 통해 State 를 변화시키는 방식으로 작동한다.
Reducer는 아래와 같이, 프로토콜을 채택하는 형태로 이루어져 있다.
// Reducer struct Feature: Reducer { struct State: Equatable { var count = 0 } enum Action: Equatable { case decrementButtonTapped } var body: some ReducerOf<Self> { Reduce { state, action in switch action { case .decrementButtonTapped: state.count -= 1 return .none } } } // OR func reduce(into state: inout State, action: Action) -> Effect<Action> { switch action { case .decrementButtonTapped: state.count -= 1 return .none } } // }
Swift
복사
구조체에 Reducer 프로토콜을 채택하게 되면 StateAction 를 만들도록 자동완성이 된다.
 Reducer 프로토콜의 내부코드
public protocol Reducer<State, Action> { /* code */ func reduce(into state: inout State, action: Action) -> Effect<Action> @ReducerBuilder<State, Action> var body: Body { get } }
Swift
복사
Reducer 를 구현하는 방법에는 두 가지 방법이 있다.

1. reduce(into: action: )

이 방법은 더 기본적인 방법을, reducer의 로직을 직접 reduce(into: action: ) 메서드 내에서 구현하는 방식이다.
다른 reducer와의 결합이 필요없는 경우에 추천되는 방법!
reducer의 로직을 직접 작성하는데 중점

2. body

더 고수준적인 방법으로, body 속성 내에서 직접 상태 변경 또는 효과 로직을 수행하지 않고, 여러 다른 reducer를 조합하는 방식으로 주로 사용된다.
해당 reducer가 추후에 더 작은 단위로 나눠진다면 이 방법을 사용하는 쪽이 더 편리하다.
더 높은 수준에서 reducer를 조합하고 기능을 구성하는데 중점
body 로 사용하게 될 경우, 연산 프로퍼티로 some Reducer 로 사용하게 될텐데, 이는 메서드와 달리 연산 프로퍼티로서 불투명 타입(Opaque Type)으로 방출할 수 있게 된다.
이 말은, 연산 프로퍼티로 만든 리듀서들끼리 조합이 가능해진다는 말!
따라서, 앱의 복잡도가 증가했을 때 더욱 이점이 있다.

 Dependency

Reducer 안에 ‘의존성’을 가리키는 Dependency 가 생기는 경우는 어떻게 처리해야할까?
ex) TCA에서 기본적으로 지원하는 Timer의 Dependency
@Dependency(\.continuousClock) var clock
Swift
복사
이렇게 Dependency 로 의존성을 주입하게 되면, Reducer 안에서 이 의존성을 활용하여, 더욱 복잡한 형태로 우리가 원하는 데이터를 편집하여 Effect 로 방출이 될 것이다.
sturct CounterFeature: Reducer { @Dependency(\.continuousClock) var clock var body: some ReducerOf<Self> { Reduce { state, action in switch action { case .toggleTimerButtonTapped: state.isTimerOn.toggle() if state.isTimerOn { return .run { send in // 주입된 의존성 활용 for await _ in self.clock.timer(interval: .seconds(1)) { await send(.timerTicked) } } .cancellale(id: CancelID.timer) } else { // Stop the timer return .cancel(id: CancelID.timer) } } } } }
Swift
복사