Swift 비동기 프로그래밍과 동시성 처리하기: DispatchQueue와 Async/Await 활용
Swift 비동기 프로그래밍과 동시성 처리하기: DispatchQueue와 Async/Await 활용
비동기 프로그래밍과 동시성 처리는 현대 애플리케이션 개발에서 매우 중요한 요소입니다. Swift에서는 이를 위해 다양한 도구와 패턴을 제공하며, 그 중에서도 DispatchQueue
와 Async/Await
는 가장 핵심적인 역할을 합니다. 이 글에서는 이 두 가지 도구에 대해 자세히 살펴보고, 이를 활용하여 효율적이고 빠른 애플리케이션을 개발하는 방법을 알아보겠습니다.
DispatchQueue: 시작하기
DispatchQueue
는 Grand Central Dispatch(GCD)의 일부분으로, 작업을 비동기적으로 실행할 때 사용하는 추상화 도구입니다. DispatchQueue
는 기본적으로 두 종류가 있습니다:
- Serial DispatchQueue: 한 번에 하나의 작업만 실행됩니다.
- Concurrent DispatchQueue: 여러 작업을 동시에 실행할 수 있습니다.
DispatchQueue
를 사용하면 복잡한 스레드 관리를 직접 하지 않아도 되므로 관련 오류를 줄일 수 있습니다. 간단한 예제를 통해 이를 더 잘 이해해 보겠습니다.
swiftlet serialQueue = DispatchQueue(label: "com.example.serialQueue") serialQueue.async { print("작업 1 시작") sleep(2) print("작업 1 종료") } serialQueue.async { print("작업 2 시작") sleep(1) print("작업 2 종료") }
위 코드에서 serialQueue
는 두 개의 작업을 순차적으로 실행합니다. 첫 번째 작업이 종료된 후에야 두 번째 작업이 시작됩니다. 반면, Concurrent DispatchQueue
는 다음과 같이 동작합니다:
swiftlet concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent) concurrentQueue.async { print("작업 1 시작") sleep(2) print("작업 1 종료") } concurrentQueue.async { print("작업 2 시작") sleep(1) print("작업 2 종료") }
concurrentQueue
는 두 개의 작업을 동시에 실행하며, 각 작업은 독립적으로 실행됩니다. 이 때문에 작업 2가 작업 1보다 먼저 종료될 수 있습니다.
Async/Await: 더 나은 비동기 프로그래밍
Swift 5.5에서는 더욱 직관적이고 관리하기 쉬운 비동기 코드를 작성할 수 있는 Async/Await
문법이 도입되었습니다. Async/Await
는 기존의 콜백 기반 비동기 코드의 복잡성을 줄여줍니다.
아래는 Async/Await
를 사용한 예제입니다:
swiftfunc fetchData() async -> String { // 네트워크 요청 시뮬레이션 await Task.sleep(2 * 1_000_000_000) return "데이터 로드 완료" } @main struct MyApp { static func main() async { let result = await fetchData() print(result) } }
위 예제에서 fetchData
함수는 비동기로 동작하며, 데이터 로드를 시뮬레이션하기 위해 Task.sleep
을 사용하고 있습니다. main
함수에서 이 비동기 함수를 호출할 때 await
키워드를 사용하여 결과를 기다립니다. 이는 마치 동기 함수처럼 보이지만 실제로는 비동기로 동작합니다.
DispatchQueue와 Async/Await 혼합 사용하기
여러 상황에서 DispatchQueue
와 Async/Await
를 함께 사용하면 더욱 강력한 비동기 프로그래밍을 구현할 수 있습니다. 예를 들어, 특정 작업을 백그라운드에서 실행하면서 UI 작업은 메인 스레드에서 유지해야 하는 경우를 생각해봅시다.
swiftfunc performBackgroundTask() async { await withCheckedContinuation { continuation in DispatchQueue.global().async { // 백그라운드 작업 수행 sleep(2) continuation.resume(returning: "작업 완료") } } } @main struct MyApp { static func main() async { let result = await performBackgroundTask() await MainActor.run { print("메인 스레드에서 결과 처리: \(result)") } } }
이 코드의 performBackgroundTask
함수는 DispatchQueue.global().async
를 사용하여 백그라운드 작업을 수행한 후, withCheckedContinuation
을 사용해 비동기 계산의 결괏값을 await
키워드로 기다립니다. 이 기능은 더 복잡한 비동기 시나리오를 단순하게 구현할 수 있게 합니다.
결론
Swift에서 비동기 프로그래밍과 동시성 처리를 위해 DispatchQueue
와 Async/Await
를 활용하는 방법을 알아보았습니다. DispatchQueue
는 적절한 스레드 관리와 동시성 제어를 가능하게 해주며, Async/Await
는 비동기 코드를 더욱 읽기 쉽고 관리하기 쉽게 만들어줍니다. 이 두 가지 도구를 적절히 조합하면 복잡한 비동기 프로그램도 효율적으로 구현할 수 있습니다. Swift로 개발하는 모든 애플리케이션에서 이러한 도구들을 적극 활용해 보세요.