2024.08.19 - [◽️ Programming/T I L] - Swift의 async/await에 대해서 알아보자
Swift의 async/await에 대해서 알아보자
Swift 5.5부터 도입된 async/await는 기존의 비동기 처리방식인 DispatchQueue나 completionHandler를 사용해 처리했지만 더욱 직관적이고 관리하기 쉽게 만들어 주는 기능이다. 기존의 비동기 처리 방식과 비
dongdida.tistory.com
이전에 async/await 에 대해서 알아보는 시간을 가졌지만 이번엔 Task는 어떤 역할을 담당하고 있는지 한번 살펴보자
Task
Task는 Swift Concurrency에서 비동기 작업을 나타내며, async/await 문법과 결합하여 동기 코드처럼 읽히면서도 내부적으로는 비동기로 실행될 수 있도록 한다!
역할은 크게 세가지로 볼 수 있는데 첫번째로 비동기 작업을 실행시키는 역할이다. Task는 비동기 함수나 작업을 시작하고, 그 결과를 기다리거나 에러를 처리할 수 있도록 한다.
두번째로 구조적으로 동시성을 띄고 있는데 Task는 계층적인 구조를 형성해서 상위 Task가 하위 Task들을 생성하고 관리할 수 있게 설정할 수 있다. 이를 통해 코드 전체의 실행 흐름과 에러, 취소 처리 등 용이한 여러가지를 가능하게 해준다.
마지막 세번째로 우선 순위를 관리해 Task 생성 후 우선 순위를 지정해 작업 간의 중요도를 관리할 수 있게 된다.
그렇다면 Task는 왜 사용하게 되는 걸까?
- 가독성과 유지보수성이 향상 된다.
기존의 GCD를 활용한 방식에서는 중첩된 클로저와 복잡한 콜백 체인으로 인해 코드의 가독성이 크게 떨어지는 경우가 종종 있었다. 하지만 Task를 사용하면 async/await 문법을 통해 마치 동기 코드처럼 순차적이고 명확하게 비동기 로직을 작성할 수 있다.
- 에러 핸들링 용이
Task 내 async 함수는 throws와 결합해서 사용될 수 있는데 이를 통해 에러가 발생했을때 do-catch 구문을 사용해 깔끔하게 에러를 처리할 수 있다는 큰 장점이 있다. 에러가 Task 트리 전체로 전파되기 때문에 상위 Task에서 하위 Task의 에러를 쉽게 관리할 수 있게 된다는 점이 아주 유용하다.
- Cancellation 지원
Task는 내장된 취소 메커니즘을 제공하는데, 작업 도중 Task.checkCancellation()을 호출하거나, Task.isCancelled를 확인해 불필요한 작업을 조기에 중단할 수 있다. 이 방법은 리소스 낭비를 막고, 사용자 경험을 향상시키는데 중요한 역할을 한다!
Task의 주요 역할 및 중요 포인트!!
- 비동기 실행 단위
Task는 비동기 작업의 가장 작은 실행 단위로, 각 Task는 독립적으로 실행되며, 결과를 반환하거나 에러를 발생시킬 수 있다. 이로 인해 복잡한 비동기 로직도 명확하게 분리하고 관리할 수 있다!!
- Structured Concurrency의 핵심이다
Swift Concurrency에서는 Task를 계층 구조로 구성함으로써, 상위 Task가 하위 Task들의 생명 주기를 관리할 수 있다. 이는 에러 전달, Cancellation 그리고 리소스 관리 측면에서 큰 이점을 제공한다. 예를 들어 상위 Task가 취소되면 그에 속한 모든 하위 Task도 함께 취소되어 예기치 않은 동시성 문제를 방지할 수 있다.
- 우선순위와 취소
Task 생성 시 우선순위를 지정하면, 시스템은 해당 작업의 실행 우선 순위를 고려해 스케줄링한다. 또한 Task 내 Task.isCancelled를 통해 현재 작업이 취소되었는지 확인 할 수 있어 불필요한 작업을 조기에 종료시킬 수 있다!
그렇다면 코드를 통해 지금까지 설명한 걸 적용해보고 이해하는 시간을 가져보자!
import Foundation
// 비동기 함수: 네트워크 요청이나 데이터 처리 등을 시뮬레이션
func fetchData() async throws -> String {
// 2초 동안 대기 (비동기 작업 시뮬레이션)
try await Task.sleep(nanoseconds: 2 * 1_000_000_000)
// 작업 도중 취소되었는지 체크
try Task.checkCancellation()
return "Fetched Data"
}
이 코드를 살펴보면 async throws를 사용해 비동기 작업 중 발생할 수 있는 에러를 처리할 수 있도록 구현되어 있는 메서드이다.
Task.sleep(nanoseconds:)를 통해 2초간 작업을 대기하면서 비동기 작업을 시뮬레이션 한다. 그 다음 Task.checkCancellation()을 호출해서 작업 도중 취소 요청이 들어왔는지 확인할 수 있다는 장점이 보인다 🙂
func loadDataAndUpdateUI() {
// 새로운 Task 생성 (구조적 동시성 내에서 실행)
Task {
do {
// 비동기 함수 호출
let data = try await fetchData()
// 메인 스레드에서 안전하게 UI 업데이트
await MainActor.run {
print("UI 업데이트: \\(data)")
}
} catch {
// 에러 처리
print("에러 발생: \\(error)")
}
}
}
두번째 메서드를 살펴보면 Task { } 블록 내 비동기 작업을 시작하며, 이는 구조적 동시성에 의해 관리되게 된다. 비동기 함수 호출 후 await MainActor.run 을 사용해 UI 업데이트를 메인 스레드에서 안전하게 수행하도록 설정할 수 있다.
그 다음 do-catch 구문을 통해 에러를 처리하고 코드의 흐름을 명확하게 유지하는 특징까지 살펴볼 수 있다!!
결론적으로 Task를 잘 사용하게 되면 비동기 코드의 복잡성을 크게 줄일 수 있고 더 안정적이고 유지보수하기 쉬운 코드를 작성할 수 있다.
이 개념을 대충은 알고 프로젝트에 적용했지만 한번 더 짚고 넘어가면서 더 이해가 되는 것 같다. Task 관련 글은 이번 한번으로 끝내는 것이 아닌, 공식문서를 토대로 더 자세한 기능과 사용, 역할에 대해서 정리할 예정이다!! 오늘은 여기까지 🙂
'◽️ Programming > iOS' 카테고리의 다른 글
Static Dispatch & Dynamic Dispatch (1/2) (0) | 2025.02.28 |
---|---|
Sendable에 대해서 알아보자 (0) | 2025.02.24 |
Objective-C를 Swift에 가져와 사용하기 (0) | 2025.02.12 |
Swift Concurrency 중 MainActor의 역할은 무엇인가 (0) | 2025.02.03 |
Vision 프레임 워크를 활용해 얼굴 인식하기 (0) | 2025.01.20 |