
안녕하세요 !
이번에는 Side Effect의 에러 처리의 주제로 포스팅 해보려 합니다
주요 내용은 TCA에서 Side Effect의 에러를 어떻게 효과적으로 처리할 수 있는지 알아보는거에요
Effect.task를 사용한 기본적인 에러 처리 방법과 여러 Side Effect를 조합할 때 발생할 수 있는 에러 처리 상황을 다루어 볼 생각입니다
TCA에서 가장 기본적인 에러 처리 방법은 Effect.task를 사용하는 건데요
struct Feature: Reducer {
struct State { var count = 0 }
enum Action {
case incrementButtonTapped
case decrementButtonTapped
case incrementResponse(Result<Int, Error>)
}
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .incrementButtonTapped:
return .task {
do {
let newCount = try await increment(state.count)
return .incrementResponse(.success(newCount))
} catch {
return .incrementResponse(.failure(error))
}
}
case .decrementButtonTapped:
state.count -= 1
return .none
case let .incrementResponse(.success(count)):
state.count = count
return .none
case .incrementResponse(.failure):
// 에러 처리 로직
return .none
}
}
func increment(_ count: Int) async throws -> Int {
// 네트워크 요청 등의 작업 수행
return count + 1
}
}
이 예제에서 .incrementButtonTapped 액션이 발생하면, increment 함수를 호출하여 카운트를 증가시켜요
이 과정에서 발생할 수 있는 에러를 try-catch 구문으로 처리하고, 결과에 따라 적절한 .incrementResponse 액션을 반환해요 ㅎㅎ
(Effect.task는 비동기 작업을 수행하고 그 결과를 처리하는 데 사용됩니다)
즉 try-catch 블록 내에서 비동기 함수 increment를 호출하고, 성공 시 새로운 카운트 값을, 실패 시 에러를 포함한 액션을 반환하는거죠
때로는 여러 Side Effect를 조합해야 할 때가 있는데 이런 경우 각 Effect의 에러를 개별적으로 처리할 수 있습니다
struct Feature: Reducer {
struct State {
var count = 0
var name = ""
}
enum Action {
case loadButtonTapped
case countLoaded(Result<Int, Error>)
case nameLoaded(Result<String, Error>)
}
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .loadButtonTapped:
return .merge(
.task {
do {
let count = try await loadCount()
return .countLoaded(.success(count))
} catch {
return .countLoaded(.failure(error))
}
},
.task {
do {
let name = try await loadName()
return .nameLoaded(.success(name))
} catch {
return .nameLoaded(.failure(error))
}
}
)
case let .countLoaded(.success(count)):
state.count = count
return .none
case .countLoaded(.failure):
// 카운트 로드 실패 처리
return .none
case let .nameLoaded(.success(name)):
state.name = name
return .none
case .nameLoaded(.failure):
// 이름 로드 실패 처리
return .none
}
}
func loadCount() async throws -> Int {
// 카운트 로드 로직
return 10
}
func loadName() async throws -> String {
// 이름 로드 로직
return "John"
}
}
위 예제는 .loadButtonTapped 액션이 발생했을 때 카운트와 이름을 동시에 로드해요
.merge를 사용하여 두 개의 Effect.task를 병렬로 실행하고 각 작업은 개별적으로 에러 처리를 수행해요
각 Effect.task 내에서 try-catch 구문을 사용하여 에러를 처리하는데 이렇게 하면 한 작업이 실패하더라도 다른 작업은 계속 진행될 수 있어요!!
예를 들어, 카운트 로드에 실패하더라도 이름 로드는 계속 진행될 수 있다는거죠
이 방법을 네트워크 통신할때 활용하면 되겠죠??
TCA에서 Side Effect의 에러 처리는 주로 Effect.run을 사용하여 수행하고
단순한 경우에는 Effect.task를 사용해요 ㅋㅋㅋㅋㅋㅋ
그럼 굳이 Effect.task 왜 이걸 알려줬냐 하면 클로저가 단일 액션을 반환하는 간단한 경우에는 Effect.task를 권장하기 때문 입니다 !!
이상으로 포스팅 마무리 하겠습니다.
.
.
.
감사합니다.