
SwiftUI로 앱을 개발하다 보면 화면이 복잡해질수록 상태 관리가 점점 어려워진다. @State, @Binding, @StateObject를 혼용해서 사용하다 보면 특정 값이 어디서, 어떤 경로로 변경되는지 추적하기 어려운 시점이 온다.
TCA(The Composable Architecture)는 이러한 문제를 해결하기 위해 Point-Free가 개발한 상태 관리 라이브러리다. 본격적인 코드를 다루기에 앞서, 이번 글에서는 TCA가 어떤 문제의식에서 출발했는지, 그리고 그 핵심 개념이 무엇인지 정리한다.
🔗 swift-composable-architecture
기존 방식에서는 View가 Model이나 State를 직접 수정할 수 있었다. 버튼을 누르면 View 코드 내부에서 model.count += 1처럼 값을 직접 변경하고, 텍스트필드에 바인딩을 걸면 사용자가 입력할 때마다 View가 State를 직접 건드린다. 문제는 앱의 규모가 커질수록 하나의 상태를 변경할 수 있는 지점이 코드 곳곳에 흩어진다는 점이다. 버그가 발생했을 때 특정 값이 왜 이렇게 변경되었는지 추적하려면, 그 값을 수정할 수 있는 모든 코드를 전부 확인해야 하는 상황이 발생한다.
TCA는 이 문제를 하나의 규칙으로 해결한다. View는 State를 직접 수정할 수 없다. View가 할 수 있는 유일한 행위는 "이런 일이 발생했다"는 신호, 즉 Action을 보내는 것뿐이며, 실제로 State를 변경하는 로직은 오직 한 곳(Reducer)에만 존재한다.
이러한 설계 원칙을 단방향 데이터 흐름(Unidirectional Data Flow)이라고 부른다. Facebook의 Flux, 그리고 Redux, Elm과 같은 아키텍처들이 공통으로 채택하고 있는 철학이며, TCA 역시 이를 그대로 따른다.
데이터는 항상 같은 방향으로 순환한다.
State가 View를 렌더링한다 → 사용자가 View에서 특정 동작을 수행한다 → View는 Action을 발송한다(State를 직접 수정하지 않는다) → Reducer가 Action을 받아 State를 변경한다 → 변경된 State가 다시 View를 렌더링한다
이 흐름에서 화살표는 절대 역방향으로 흐르지 않는다. View에서 State로 직접 연결되는 경로는 존재하지 않으며, 모든 변경은 반드시 Action이라는 경로를 거쳐야 한다. 이렇게 강제함으로써 두 가지 이점을 얻을 수 있다. 첫째, State를 변경하는 코드가 한 곳에 모여 있으므로 버그 추적이 용이해진다. 둘째, 모든 변화가 Action이라는 명시적인 이벤트로 기록되므로, 앱이 어떤 상태를 거쳐 현재에 이르렀는지를 Action의 나열만으로 재구성할 수 있다.
참고로 처음 학습할 때 단방향 데이터 흐름을 클린 아키텍처와 혼동하기 쉬운데, 두 개념은 서로 다르다. 클린 아키텍처는 계층 간 의존성 방향에 대한 규칙(바깥 계층이 안쪽 계층에 의존해야 한다)을 다루고, 단방향 데이터 흐름은 상태가 어떻게 변경되는가에 대한 규칙을 다룬다. 완전히 무관하지는 않지만 서로 다른 축의 개념으로 이해해야 한다.
State
— 화면에 필요한 데이터를 담은 구조체다. 현재 화면이 어떤 모습이어야 하는지를 나타낸다.
Action
— 발생 가능한 모든 이벤트를 나열한 열거형이다. 버튼 탭, 네트워크 응답 도착 등이 여기에 해당한다.
Reducer
— Action이 들어왔을 때 State를 어떻게 변경할지 정의하는 로직이다. State 변경 코드는 전부 이곳에 모인다.
Store
— State와 Reducer를 하나로 묶어 View에 전달하는 컨테이너다. View는 이 Store를 통해서만 State를 읽고 Action을 발송한다.
Effect
— 네트워크 호출과 같이 비동기로 처리해야 하는 부수 효과다. Reducer는 순수 함수여야 하므로, 비동기 작업은 Effect로 감싸서 처리하고 결과가 도착하면 다시 Action으로 전달한다.
이번 글에서는 TCA가 어떤 문제의식에서 출발했는지, 그리고 그 해법인 단방향 데이터 흐름의 개념을 정리했다. 다음 글에서는 State, Action, Reducer를 실제 코드로 작성하며 이 다섯 가지 요소가 어떻게 맞물려 동작하는지 살펴본다.