주로 SwiftUI (MVVM) 로 개발을 진행하고 있는데 직장 동료의 조언에 따라 다른 아키텍처를 사용 해보고자 한다.
이번엔, 선언형 프로그래밍에서 많이 사용하고 있는 TCA 라는 아키텍처를 연습해보고 알아보자.
여기서 TCA(The Composable Architecture) 란?
기존에 리액터에서 사용하는 기법으로 단방향으로 사이클이 돌아가며,
ReactorKit 과, Redux 과 같은 비슷한 아키텍처 이다, Swift (UIKit, SwiftUI, Watch, TV, Mac OS 모두 사용이 가능하다.)
내부적으로 Combine 을 사용하고 있기때문에 최소 버전은 iOS 13 버전이다.
* 필자가 공부한 내용 토대로 작성 하였기에, 내용이 그때그때 수정될수 있으며 사실과 다른내용이 있을수도 있습니다.
틀린 내용 발견시 댓글 달아주세요!
TCA 의 구성도는 다음과 같다.
- View : State의 상태 변화를 구독 해놓고, UI 를 그리는 구간 어떠한 행위 발생시 Action 에 전달한다.
- Action : UI 에서 요청한 이벤트를 담아 Reducer 에 전달한다.
- Reducer : 실제로 발생할 비즈니스 로직을 담당하는 구간 (대체로 API 통신 을 많이 한다)
* 따로 사이드 이펙트가 발생할 로직이 없는경우 (API 통신) State 에 값을 전달하여 바로 뷰를 갱신 시킬수 있다.
- Effect : 사이드 이펙트 발생후, 다시 액션을 요청해 로직을 진행할수 있다
ex) API 조회후, Response Result, Error 에 대해 Action 을 따로 처리할때 사용 할수 있다.
- Environment : 외부 의존성 주입이 필요할때 추가 (API Client, DispathQueue, UUID 등등)
- State : UI 의 상태를 변경시킬 변수를 나열해 놓는다, 값의 상태 변경은 Reducer 에서 처리 해주는거같다.
아래는 TCA를 이용한 간단한 카운터 앱이다 (일단 맛만 보자)
1. TCA 라이브러리 설치
- SPM 을 이용해 다음 라이브러리를 설치한다. https://github.com/pointfreeco/swift-composable-architecture
2. Domain 구성 (State, Action, Environment)
- API 통신과 기타 로직이 없기 때문에 Environment 는 공란이다.
- State는 View 에서 값을 변경 처리할 count 를 추가 하였다.
- View 에서 전달받은 Action에 대해 Reducer 가 전달받아 State 상에 값을 던져준다
- State 값을 뷰에 적용한다. (3번 참고)
import Foundation
import ComposableArchitecture
struct CounterEnvironment: Equatable { }
enum CounterAction {
case add
case sub
}
struct CounterState: Equatable {
var count = 0
}
let counterReducer = AnyReducer<CounterState, CounterAction, CounterEnvironment> {
state, action, environment in
switch action {
case .add:
state.count += 1
return .none
case .sub:
state.count -= 1
return .none
}
}
3. View
- 뷰는 WithViewStore 로 감싸져있으며, Store 값이 변경되면 뷰의 변경 사항이 감지된다.
- 여기서 Store 를 선언 해주어 Store 영역과 View 를 연결 시켜준다.
struct CounterView: View {
let store: Store<CounterState, CounterAction> = Store(initialState: CounterState(), reducer: counterReducer, environment: CounterEnvironment())
var body: some View {
WithViewStore(self.store) { viewStore in
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("counter : \(viewStore.count)")
HStack {
Text("+")
.onTapGesture {
viewStore.send(.add)
}.padding(.trailing, 20)
Text("-")
.onTapGesture {
viewStore.send(.sub)
}
}
}
.padding()
}
}
}
마치며
새로운 개발 방법은 나를 항상 가슴뛰게 만든다, 해당 기술을 실제 프로젝트에 적용 할지는 고민좀 해봐야겠지만,
공부를 해볼 필요는 있다 생각한다. 지금은 로컬에서 동작할만한 로직만 추가 하였으나, 다음 포스팅에는 API 추가 하여 한발짝 더 걸어나가보자.
출처 :
https://github.com/pointfreeco/swift-composable-architecture
https://www.youtube.com/watch?v=fYQ9YnbvasU
https://www.youtube.com/watch?v=vU_pRzQMoho
'IOS' 카테고리의 다른 글
iOS - SPM 프로젝트에 Cocoapods 을 달아보자 (1) | 2023.08.13 |
---|---|
Swift - Google Login (0) | 2023.06.24 |
SwiftUI - 이미지 줌 기능을 달아보자 (0) | 2023.02.26 |
iOS - 프로젝트에 SPM 을 달아보자 (0) | 2023.02.19 |
Xcode - Google Sheet 에서 다국어 내용 가져오기 (0) | 2023.01.23 |