iOS/TCA

[TCA] 1. The Composable Architecture ?

여성일 2025. 10. 17. 22:52
728x90
TCA가 뭘까요?

The Composable Architecture (TCA, for short) is a library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind. It can be used in SwiftUI, UIKit, and more, and on any Apple platform (iOS, macOS, iPadOS, visionOS, tvOS, and watchOS). - TCA

 

 TCA(The Composable Architecture)는 Composition, Testing, Ergonomics을 고려하여 일관성 있고 이해하기 쉬운 방식으로 앱을 구축하기 위한 라이브러리입니다. SwitfUI, UIKit에서 사용할 수 있으며 Apple의 어느 플랫폼(iOS, macOS, tvOS, watchOS)에서도 사용 가능합니다. 

 

라고 하는데.. 말이 좀 어렵죠? 합성, 테스팅, 인체공학? .. 천천히 살펴 봅시다. 

 

1. Composable

Composable의 사전적 의미는 구성 가능한, 조합할 수 있는이라는 뜻인데요, 말그대로 작은 것들이 모여 큰 것을 만든다라고 생각하면 될 것 같습니다. 개발적으로 생각해보면 큰 기능들을 분리해 모듈화하고 이를 조합하고 구성한다는 의미로 해석할 수도 있겠네요.

 

2. SwiftUI와 같이 사용하면 좋다.

TCA 내부적으로 Combine이 사용되고 있고, SwiftUI와 동일한 철학(선언형 패러다임)을 공유하기 때문입니다. SwiftUI가 UI를 상태의 결과물을 선언하는 역할이라면, TCA는 그 상태를 관리하고 변경하는 로직을 선언하는 역할을 합니다. 같은 철학을 공유하기에 같이 사용하면 좋겠죠?

 

물론 UIKit에서도 TCA 사용이 가능합니다!

 

3. 테스팅이 용이하다.

TCA는 비즈니스 로직을 예측 가능한 Reducer로 분리하고 비동기 작업을 캡슐화하여, UI 없이도 모든 동작을 쉽고 안정적으로 테스팅할 수 있습니다. 

 

왜 TCA를 선택할까요?

새로운 패러다임 

TCA를 본격적으로 이야기 하기 전에 SwiftUI에 대해 잠깐 이야기해볼까요? SwiftUI는 선언적 UI 프레임워크로, 기존의 UIKit의 명령형 방식에서 벗어나, UI를 상태의 결과물로 정의하는 새로운 패러다임을 열었습니다.

 

 예를 들어, 기존의 UIKit은 "버튼을 누르면, 텍스트 필드의 내용을 가져와서, 레이블의 텍스트를 이걸로 바꿔!"와 같이 하나하나 명령하며 UI를 변경했습니다. 하지만 SwiftUI는 데이터(State)가 변경되면, SwiftUI가 알아서 UI를 다시 그려줍니다. 우리는 그저 상태와 UI의 관계만 선언하면 됩니다.

양방향 통신

 SwiftUI의 기본 상태 관리 도구(@State, @Binding)는 양방향 통신을 합니다. @Binding을 사용하면 하위 뷰가 상위 뷰의 상태를 직접 수정할 수 있습니다. 이는 구조가 간단한 앱에서는 편리하지만, 구조가 복잡한 앱에서는 데이터의 흐름을 추척하기 어려워집니다.

MVVM

 MVVM 이야기를 안 할수 없겠네요.. iOS에서 가장 대중적인 아키텍처 중 하나죠? 하지만 SwiftUI와 결합될 때, ViewModel의 상태를 어떻게 관리하고 뷰의 입력을 어떻게 처리하느냐에 따라 여전히 양방향 데이터 흐름의 함정에 빠지기 쉽습니다.

 

 저도 SwiftUI에서 MVVM은 지양하자는 의견인데요, 우리 애플 아카데미 전 테크멘토이신 Young의 좋은 글이 있습니다. 꼭꼭 한번 읽어보셨으면 좋겠어요!

단방향 통신 

 양방향 통신의 복잡성을 해결하기 위한 아이디어가 바로 단방향 통신입니다. 명칭 그대로 데이터가 한 방향으로만 흐르도록 하는 방법입니다. 단방향 통신은 데이터의 변화가 예측 가능한 사이클을 따르기 때문에 상태 변화를 추적하고 디버깅하기 매우 용이합니다.

 

제가 여기서 이야기하고 싶은 것은 "단방향 플로우가 양방향 플로우 보다 좋다"를 이야기하고 싶은 것은 아닙니다. 양방향 플로우가 상대적으로 복잡하고 데이터 흐름을 추적하기 어렵다는 이야기에는 공감하지만, 그렇다고 단방향 플로우가 모든 문제를 해결해준다고 생각하지는 않습니다.

 

 기존의 명령형 방식에서 벗어나, 선언형 방식으로 옮겨가는 이 과정(과도기..?)에 있어서 기존 양방향 플로우를 채택하는 것이 과연 좋은 방법일까? 하는 의구심은 들긴 합니다요!

 

 실제로 제가 지금 활동하고 있는 애플 아카데미에서도 단방향 통신에 대해 러너들과 많은 이야기를 나누고 있습니다. SwiftUI에서 Binding을 구현할 때, View에서 프로퍼티 래퍼(@State, @Binding)로 구현할 수 있는데 굳이 ViewModel을 사용해야할까? 단지 파일을 하나 더 만드는 것은 아닐까? ➡️ 하지만, 비즈니스 로직이나 사이드 이펙트는 View에서 처리하기엔 View가 부담스럽지 않을까? ➡️ 이러한 이유로 단방향 플로우가 필요한 것은 아닐까? 하는 .. 저만의 생각입니다..!

 

❗️ 그렇다면 왜 TCA를 선택할까?

TCA(The Composable Architecture)는 앞서 살펴본 단방향 데이터 흐름의 철학을 완전히 구현한 아키텍처입니다. 

- 예측 가능성: 모든 상태 변화가 단방향으로 흐르기 때문에 앱의 동작을 예측하고 이해하기 쉽습니다
- 테스트 용이성: 순수 함수 기반의 구조로 비즈니스 로직을 쉽게 테스트할 수 있습니다
- 모듈화와 조합성: 기능을 독립적인 단위로 나누고, 이를 조합하여 복잡한 앱을 구성할 수 있습니다
- 일관된 패턴: 팀 전체가 동일한 구조와 규칙을 따르므로 협업과 유지보수가 수월합니다

Composable Architecure의 단방향 플로우

 TCA는 일반적으로 5가지로 구성되어 있습니다.

- State : 비즈니스 로직을 수행하고 UI를 렌더링하는 데에 필요한 데이터 타입

- Action : 사용자 액션, 알림 등 앱에서 발생하는 모든 이벤트(액션)을 나타내는 타입으로, 열거형으로 정의합니다.

- Reducer : 액션이 주어졌을 때 앱의 현재 상태를 다음 상태로 업데이트하거나 Effect(네트워크 통신 등)를 반환합니다.

- Store : 실제로 기능이 작동하는 곳으로, 모든 사용자 액션을 스토어로 전송하면 스토어는 리듀서와 Effect를 처리할 수 있고, 상태 변화를 관찰하여 UI를 업데이트할 수 있습니다.

- Environment : 외부 시스템과 통신을 담당하고, 기능에 필요한 종속성을 보유하는 타입

 

아카데미에서 배운 피그마로 뚝딱 만들어버리기..

 TCA의 단방향 플로우는 일반적으로 위와 같습니다! View에서부터 시작해봅시다. View는 Action을 발생시킬 수 있습니다. 사용자가 텍스트를 입력하거나, 버튼을 누르거나, 스크롤하거나.. 하면 Action이 발생하겠죠? View는 Store를 구독하고 있기 때문에 View에서 발생한 Action은 Store로 전달 됩니다. 쉽게 생각하면 Store가 Action을 감지한다고 생각하면 되겠네요! 

 

 Store는 Action을 감지하여(감지한다는 표현이 맞을까 싶긴 하네요.. 전달 받는다?) Reducer를 통해 Action을 처리합니다. Reducer는 Action의 형태에 따라 상태를 업데이트하거나 Effect를 처리합니다.