버그 잡이

TCA란 무엇이고 어떻게 Composable 할 수 있는가? 본문

Swift

TCA란 무엇이고 어떻게 Composable 할 수 있는가?

버그잡이 2023. 5. 27. 17:59

TCA 란?

- '일관적이고 이해할 수 있는 방법으로 앱을 개발할 수 있게 도와주는 라이브러리'

- composition, testing, ergonomics(인체 공학)를 염두해두고 만들었다고 합니다.

 

* 구조

https://hashnode.com/post/build-an-itunes-search-app-with-swiftui-and-the-composable-architecture-part-1-ckbflzbkd01oodis1hwaf6o3j

 

1. 사용자의 동작은 View를 통해 Action의 형태로 변환됩니다.

2. Action은 Reducer에서 처리되어 State를 바꿉니다.

3. State를 구독하고 있는 View는 State의 변화에 따라 최신화됩니다.

4. Environment는 외부 의존성이라고 생각하면 됩니다. Environment를 Effect로 변환해서 Action을 실행시킵니다.

 

* Effect

- Composable 외부에서 일어난 일을 가져와서 상태를 변경시킬때 사용

- 외부 접근 = API , Disk, Timer 등등

 

 

 

TCA 왜 쓰나요? (장점)

 

1. 강제되는 룰이 있기 때문에 코드 통일성 높아집니다.

 

2. 단 방향 아키텍처이기 때문에 Reducer를 테스트하기 좋습니다.

 

3. composable 하기 때문에 다른 도메인에 쉽게 연결될 수 있고 재사용 될 수 있습니다.

 

 

 

어떻게 composable 할 수 있는가?

 

오늘 글의 핵심 주제인데요. 저는 TCA(The Composable Architecture)란 이름을 보자마자 이 친구가 어떻게 Composable한지 궁금했습니다.

Composable하다는 것은 레고처럼 조립해서 만들 수 있다는 것인데, 어떻게 이런 구조를 가져갈 수 있는지 알아보겠습니다.

 

이를 가능하게 해주는 Pullback이라는 오퍼레이터에 대해서 간단하게 개념만 살펴보고 코드를 살펴보겠습니다.

 

Pullback

pullback은 다른 도메인의 Reducer를 가져와서 컨트롤 할 수 있게 도와주는 오퍼레이터입니다.

다른 도메인의 액션으로 State변경 및 다른 액션으로 매칭 시켜주는 처리를 가능하게 합니다.

 

예시
https://github.com/pitt500/OnlineStoreTCA/tree/main

(코드와 사진을 위 깃헙에서 가져왔습니다.)

https://www.youtube.com/watch?v=Zf2pFEa3uew

위 화면은 크게 두 개의 View로 구성되어있습니다.

1. +/- 클릭이 가능한 PlusMinusButton (위 사진에서 빨간색 테두리 영역)

2. 위 버튼을 가지고 있는 ProductView

ProductView에 PlusMinusButton이 하위뷰로 구성된 것이죠.

이 경우 +, - 버튼에 관한 Action, State로직은 (여기서는 이를 통틀어 Domain이라고 칭하겠습니다.) PlusMinusButton에서 처리될텐데요.

이를 상위뷰인 ProductView에서 접근하고 싶습니다. 이 깃헙 예제에서는 해당 값이 0보다 작은 값이 되지 않도록 하는 처리를 상위뷰에서 처리해줬습니다.

 

PlusMinusButton는 아래와 같은 Domain을 가지고 있습니다.
count라는 State가 있고 이를 +, - 해주는 didTapPlusButton, didTapMinusButton 액션을 가지고 있습니다.

struct AddToCartDomain {
    struct State: Equatable {
        var count = 0
    }
    
    enum Action: Equatable {
        case didTapPlusButton
        case didTapMinusButton
    }
    
    struct Environment {}
    
    static let reducer = Reducer<
        State, Action, Environment
    > { state, action, environment in
        switch action {
        case .didTapPlusButton:
            state.count += 1
            return .none
        case .didTapMinusButton:
            state.count -= 1
            return .none
        }
    }
}

 

 

AddToCartDomain의 하위 도메인을 상위 도메인인 ProductDomain에서 사용해보겠습니다.

struct ProductDomain {
    struct State: Equatable, Identifiable {
        let id: UUID
        let product: Product
        var addToCartState = AddToCartDomain.State()
    }
    
    enum Action: Equatable {
        case addToCart(AddToCartDomain.Action)
    }
    
    struct Environment {}
    
    static let reducer = Reducer<
        State, Action, Environment
    >.combine(
        AddToCartDomain.reducer
            .pullback(
                state: \.addToCartState,
                action: /ProductDomain.Action.addToCart,
                environment: { _ in
                    AddToCartDomain.Environment()
                }
            ),
        .init { state, action, environment in
            switch action {
            case .addToCart(.didTapPlusButton):
                return .none
            case .addToCart(.didTapMinusButton):
                state.addToCartState.count = max(0, state.addToCartState.count)
                return .none
            }
        }
    )
}

 

State, Action에서 AddToCartDomain의 State와 Action을 추가해주고
reducer에서 .combine() 오퍼레이터로 AddToCartDomain의 reducer를 머지해줍니다.

그리고 위에서 말한 .pullback()을 사용하는데요. 이를 통해서 하위 reducer들이 부모 reducer에서 사용될 수 있는 것입니다.

 

이렇게 각각의 도메인을 조합해서 사용하면 도메인이 비대해지는 것을 막을 수 있어 유지보수하기 좋은 코드가 될 것 같네요.

 

 

요약

1. TCA는 Composition, Test를 염두해둔 단방향 아키텍처다.

2. .pullback()을 활용해서 다른 Reducer를 조립해서 쓸 수 있기 때문에 composable하다.

3. (Test는 얼마나 편리한지 알아보자)

 

(공부 정리용으로 기록한 글입니다. 잘못된 내용이 있다면 댓글로 남겨주세요.)

 

 

* 참고
https://github.com/pitt500/OnlineStoreTCA/tree/main
https://github.com/pointfreeco/swift-composable-architecture

https://green1229.tistory.com/282

https://0urtrees.tistory.com/359

반응형
Comments