5.1 TCA와 Dependency

먼저, 본 내용을 다루기에 앞서, Dependency란 무엇인지 짚고 넘어가야 할 필요가 있습니다. Dependency는 친숙한 말로 의존성이라고 하며, 이러한 의존성을 처리하기 위해 의존성 주입과 같은 방식으로 처리해 오고 있습니다. 이러한 의존성에 대한 대표적인 예시로 네트워크 통신의 사례가 있으며, 파일 액세스, userDefault, 심지어 이전 챕터에서 다룬 시계 및 타이머와 같은 것들 또한 Dependency라고 할 수 있습니다. 따라서 의존성에 대한 관리는 앱 개발 전반에 중대한 영향을 미칠 수 있습니다. 대표적인 예시로 네트워크 요청이 있습니다. 네트워크 요청의 경우, 네트워크 연결, 인터넷 속도 등 실제 통제할 수 없는 요소들이 너무나 많고 이로 인해 테스트 하기 어렵다는 문제점이 존재합니다. 또한 SwifUI의 경우, 프리뷰 기능을 제공하지만, preview를 위한 mock과 같이 별도의 의존성을 처리해주지 않는다면 프리뷰를 통한 앱개발에 어려움이 있을 수 있습니다. 따라서, 적절하게 통제되지 못한 애플리케이션내의 의존성으로 발생하는 문제점들을 해결하기 위해 TCA에서는 다음과 같은 라이브러리를 제공하고 있습니다.

바로, TCA Dependency 입니다. TCA Dependency는 앱개발에서 의존성을 보다 쉽게 관리할 수 있도록 다음 아래와 같은 부분들을 고려하여 만들어진 의존성 관리 라이브러리 입니다.

<aside> 💡 TCA Dependency 라이브러리의 고려 사항

이제 본격적으로 Dependency 라이브러리에 대해 알아보기전에, 먼저 Dependency 도입 이전에 어떻게 의존성을 관리했는지 간단하게 살펴보고, TCA에서 Dependency의 원리와 적용 방식에 대해 차례대로 살펴보도록 하겠습니다.


5.1.1 ReducerProtocol 이전의 Dependency 관리 방식

ReducerProtocol이 도입되기 이전에, Reducer를 구성하는 요소는 상태 값들을 관리하는 State와 이러한 상태의 변이 및 비즈니스 로직을 수행하는 Action과 더불어, 마지막 요소인 의존성을 관리하는 Environment라는 명칭을 가진 구조체로 이루어져 있었습니다. 하지만, 기존의 Environment를 통해 애플리케이션에 필요한 의존성을 관리하는 경우, 만약 해당 리듀서에서 추가적인 의존성이 필요해지는 상황이 발생한다면, 추가해야 할 많은 보일러 플레이트 코드가 발생할 수 있고, 빌드 단계에서 컴파일 에러를 유발하는 문제 상황이 발생할 수 있습니다. 이 같은 상황을 아래 예시 코드를 통해 살펴보도록 하겠습니다.

예시로, SwiftUI 와 TCA로 구축된 오픈 소스 단어 게임인 isowords의 일부 코드를 통해 살펴보도록 하겠습니다. 아래는 isowords에서 설정 기능을 담당하는 Settings라는 Feature Reducer에 대한 SettingsEnvironment 입니다. 해당 Environment에서는 SettingsReducer에서 필요한 apiClientfileClient와 같은 외부 서버와의 네트워크 통신 및 여러 의존성들을 관리하고 있습니다.

struct SettingsEnvironment {
	public var apiClient: ApiClient
	public var fileClient: FileClient
	public var userDefaults: UserDefaultsClient
  public var userNotifications: UserNotificationClient
	/* code */
}

만약 위의 상황에서, SettingsFeature가 다른 추가적인 의존성을 필요로 한다면, 다음과 같이 새롭게 추가된 의존성을 적용시키기 위해 아래와 같이 코드를 작성해 볼 수 있습니다.

struct SettingsEnvironment {
	public var apiClient: ApiClient
	public var fileClient: FileClient
	public var userDefaults: UserDefaultsClient
  public var userNotifications: UserNotificationClient
	/* code */
  // 추가된 additionalClient 
	public var additionalClient: AdditionalClient
}

하지만, SettingsEnvironment가 구조체 타입으로 다루어지고 있기 때문에, init 생성자에 새롭게 추가된additionalDependency에 대한 코드를 일일이 추가적으로 작성해주어야 한다는 약간의 문제점이 있습니다.

struct SettingsEnvironment {
	/* code */
	init(additionalClient: AdditionalClient, /* code */) {
	  self.additionalClient = AdditionalClient
	}
}