안녕하세요.
오늘은 Swift의 Concurrency 기능 중 하나인 withCheckedContinuation을 활용한 비동기 작업을 직접 만들어본 경험을 정리해보려고 합니다.
저는 이전에 Swift의 비동기 작업을 처리할 때 주로 completion handler 방식을 사용했습니다. 그런데 비동기 로직이 많아질수록 콜백 지옥이 펼쳐지고, 코드가 점점 복잡해지는 문제를 마주하게 됐습니다. 그러다가 최근 과제전형을 진행중에 기존의 completion handler 방식을 async로 바꿔주는 withCheckedContinuation이라는 기능을 알게 되었고, 이 콜백 지옥을 탈출할 수 있을거 같다는 희망을 발견해 작성을 하게 되었습니다.
먼저 간단한 예시를 들어볼게요. API 요청을 completion handler 방식으로 작성하면 다음과 같이 됩니다.
func fetchData(completion: @escaping (Result<String, Error>) -> Void) {
DispatchQueue.main.async {
completion(.success("데이터 로딩 완료"))
}
}
// 호출 부분
fetchData { result in
switch result {
case .success(let data):
print(data)
case .failure(let error):
print(error.localizedDescription)
}
}
이 방식은 코드가 길어지고, 중첩될수록 가독성이 떨어지는 단점이 있었습니다.
withCheckedContinuation을 이용하면 위 코드를 간단히 async/await 스타일로 변경할 수 있습니다.
func fetchData() async throws -> String {
return try await withCheckedContinuation { continuation in
fetchData { result in
switch result {
case .success(let data):
continuation.resume(returning: data)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
// 호출 부분
Task {
do {
let data = try await fetchData()
print(data)
} catch {
print(error.localizedDescription)
}
}
이 코드에서는 기존의 completion handler를 continuation이라는 객체로 변환하고, 결과를 명확히 해서 async 함수로 바꾸었습니다.
음.. 예제를 너무 간단한걸 했더니 아래가 좀 더 복잡해 보일 수 있지만 실제로 사용할 때는 아래가 좀 더 코드가 더 깔끔하고 읽기 쉬워졌다는걸 알 수 있었습니다.
이번 경험을 통해 주로 completion handler를 주로 사용해와서 자주 빠졌던 콜백지옥에서 벗어날 수 있다는 작은 희망을 발견했습니다. 앞으로는 Swift Concurrency를 더욱 적극적으로 사용해서 코드 품질을 높이고 유지보수를 쉽게 만들어야겠다는 생각이 듭니다.
다음 글에서는 Combine과 Swift Concurrency를 결합하여 더 다양한 상황에서의 비동기 처리도 한 번 정리해볼 예정입니다.
감사합니다!