DispatchQueue는 iOS 및 macOS에서 비동기 작업을 관리하기 위한 Grand Central Dispatch(GCD)의 기본 큐입니다. Apple Developer Documentation, 2026에 따르면, DispatchQueue는 직렬 및 동시 큐를 통해 개발자로부터 스레드 관리를 추상화합니다. GCD는 시스템 스레드 풀에 작업을 자동으로 분산하여 수동 스레드 생성 및 제거를 없앱니다.
핵심 요점
DispatchQueue는 시스템 또는 사용자 정의 스레드 큐에서 작업 실행을 관리하는 Grand Central Dispatch(GCD) 프레임워크의 객체입니다. Grand Central Dispatch는 iOS 4 및 macOS 10.6부터 사용 가능한 Apple의 저수준 라이브러리로, 개발자로부터 스레드 관리를 완전히 추상화합니다. GCD는 운영 체제 스레드 풀을 사용하며 장치 부하에 따라 스레드 수를 자동으로 조정합니다.
개발자가 수동으로 스레드를 만들고 제거할 필요가 없습니다 — GCD가 이 작업을 처리하며 DispatchQueue를 통해 간단한 API를 제공합니다. 클로저 형태의 작업은 sync 또는 async 메서드를 통해 큐에 전송됩니다. 첫 번째 경우 호출 스레드는 작업이 완료될 때까지 차단되고, 두 번째 경우 실행이 즉시 계속됩니다.
Apple(2026)에 따르면, GCD는 코어 수와 현재 CPU 부하에 적응하는 시스템 스레드 풀을 사용합니다. 동시 큐는 각 작업에 대해 새 스레드를 만들지 않습니다 — GCD는 풀에서 스레드를 재사용하여 스레드 생성 오버헤드를 최소화합니다.
Grand Central Dispatch는 세 가지 주요 구성 요소로 이루어져 있습니다: 큐(DispatchQueue), 그룹(DispatchGroup), 세마포어(DispatchSemaphore). 큐는 코드 블록 형태로 작업을 받는 주요 요소입니다. DispatchGroup은 여러 작업의 실행을 동기화하고, DispatchSemaphore는 공유 리소스에 대한 액세스를 특정 스레드 수로 제한합니다.
각 GCD 큐는 특정 QoS(Quality of Service) 클래스와 연결되어 시스템에 작업의 중요성을 알립니다. 시스템은 큐 간에 CPU 시간을 분배하기 위해 QoS를 사용하며, UI 업데이트나 사용자 터치 처리 같은 더 중요한 작업에 우선순위를 부여합니다.
직렬 큐는 작업을 엄격하게 순차적으로 실행합니다. 직렬 큐에 세 개의 작업을 넣으면 두 번째 작업은 첫 번째 작업이 완전히 완료된 후에만 시작됩니다. 직렬 큐는 공유 리소스에 대한 액세스를 동기화하는 데 사용됩니다 — 예를 들어 코드의 여러 부분에서 수정되는 배열.
동시 큐는 여러 작업을 동시에 실행하여 시스템 풀의 사용 가능한 스레드에 분배합니다. 동시 큐의 작업은 FIFO 순서로 시작되지만 실행 시간이 다르면 임의의 순서로 완료됩니다. 동시 큐는 완료 순서를 보장하지 않습니다 — 시작 순서만 보장합니다.
| 매개변수 | 직렬 큐 | 동시 큐 |
|---|---|---|
| 실행 순서 | 엄격하게 순차적 | 병렬 |
| 스레드 수 | 하나 | GCD 풀에서 여러 개 |
| 용도 | 공유 리소스 보호 | 독립적인 계산 |
| 메인 큐 | 예(메인 스레드) | 아니요 |
| 데드락 위험 | 같은 큐에서 sync 시 높음 | 낮음 |
직렬 큐는 파일 쓰기, 데이터 모델 업데이트 또는 Core Data 작업과 같이 공유 상태를 수정하는 작업에 이상적입니다. 직렬 큐를 사용하면 코드의 두 부분이 동시에 동일한 데이터를 수정하지 않도록 보장하여 추가 잠금 없이 경쟁 조건을 방지합니다.
동시 큐는 서로 의존하지 않는 작업에 적합합니다: 여러 이미지 로드, 병렬 네트워크 요청 또는 데이터 일괄 처리. GCD는 CPU 코어 수와 현재 시스템 부하에 따라 동시에 실행할 작업 수를 자동으로 결정합니다.
QoS(Quality of Service)는 작업의 중요성과 긴급성을 운영 체제에 알리는 GCD 메커니즘입니다. 시스템은 스레드 스케줄링에 QoS를 사용합니다: 높은 QoS의 작업은 더 많은 CPU 시간을 받고 먼저 시작됩니다. QoS 값은 큐를 만들거나 특정 작업을 제출할 때 전달됩니다.
GCD에서는 5개의 QoS 클래스를 사용할 수 있습니다. .userInteractive — UI 관련 작업을 위한 가장 높은 우선순위. .userInitiated — 사용자가 시작한 작업용. .utility — 진행 상황을 표시하는 백그라운드 작업용. .background — 사용자에게 보이지 않는 작업용. .default — userInitiated와 utility 사이의 중간 수준으로 기본적으로 사용됩니다.
Apple(2026)에 따르면, 잘못된 QoS 선택은 성능 문제의 일반적인 원인 중 하나입니다. QoS .userInteractive로 백그라운드 다운로드를 실행하면 UI의 리소스를 소비하여 애니메이션에 마이크로 지연이 발생합니다. 허용 가능한 실행 시간을 제공하는 가장 낮은 QoS를 선택하는 것이 좋습니다.
즉시 표시할 이미지를 로드할 때는 .userInitiated를 사용합니다 — 사용자가 결과를 기대합니다. 다음 화면을 미리 로드할 때는 .utility로 충분합니다. 백그라운드 서버 동기화는 .background로 실행되어 활성 작업에 미치는 영향을 최소화합니다.
DispatchGroup은 작업 그룹의 완료를 추적할 수 있게 합니다. 그룹의 모든 작업이 완료되면 GCD는 지정된 큐에서 notify 핸들러를 호출합니다. 이는 여러 독립 리소스(프로필 데이터, 친구 목록, 설정)를 로드할 때 특히 유용하며, 모든 데이터를 수신한 후에만 인터페이스를 업데이트해야 합니다.
DispatchGroup은 동기 wait() 호출을 지원하여 모든 작업이 완료될 때까지 현재 스레드를 차단합니다. 이는 그룹의 결과 없이 코드를 계속할 수 없을 때 편리합니다. 비동기 변형인 notify()는 모든 작업이 완료된 후 지정된 큐에서 클로저를 호출하며 호출 스레드를 차단하지 않습니다.
DispatchSemaphore는 동시 액세스 수를 제한하여 리소스에 대한 액세스를 제어합니다. 초기값 3의 세마포어는 최대 3개의 병렬 작업을 허용합니다. wait()를 호출하면 카운터가 감소하고 signal()이 증가합니다. 카운터가 0이 되면 리소스를 사용할 수 있을 때까지 스레드가 차단됩니다.
Swift에서 DispatchQueue를 사용하는 세 가지 실제 예제를 살펴보겠습니다. 첫 번째는 메인 스레드로 돌아오는 기본 async 호출, 두 번째는 직렬 큐를 통한 동기화, 세 번째는 병렬 요청을 위한 DispatchGroup입니다.
DispatchQueue.main은 메인 스레드의 직렬 큐로, UI 작업 전용입니다. 백그라운드 작업을 완료한 후 인터페이스를 업데이트하려면 항상 이것을 사용하세요.
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
let data = self.fetchData()
DispatchQueue.main.async {
self.updateUI(with: data)
}
}
고유 식별자로 사용자 정의 직렬 큐를 만들면 변경 가능한 배열에 대한 액세스가 동기화됩니다. 모든 읽기 및 쓰기 작업은 단일 큐를 통과하여 경쟁 조건을 없앱니다.
let serialQueue = DispatchQueue(label: "com.app.items")
var items: [Int] = []
serialQueue.async {
items.append(1)
}
serialQueue.async {
let last = items.last
DispatchQueue.main.async {
print("Last item: \(last)")
}
}
DispatchGroup을 사용하면 동시 큐에서 여러 작업을 시작하고 모두 완료되면 알림을 받을 수 있습니다. 프로필 화면의 데이터를 로드할 때 유용합니다.
let group = DispatchGroup()
let worker = DispatchQueue.global()
worker.async(group: group) { self.loadProfile() }
worker.async(group: group) { self.loadFriends() }
worker.async(group: group) { self.loadSettings() }
group.notify(queue: DispatchQueue.main) {
self.showCompleteUI()
}
직렬 큐에서 sync를 호출할 때의 데드락은 가장 흔한 실수입니다. 직렬 큐의 작업이 동일한 큐에서 queue.sync를 호출하면 스레드가 영원히 차단됩니다. 큐는 현재 작업의 완료를 기다리고, 작업은 sync 호출의 완료를 기다립니다 — 전형적인 상호 차단입니다.
UIKit의 모든 작업은 메인 스레드에서 수행되어야 합니다. Xcode는 Main Thread Checker를 통해 디버그 모드에서 이러한 오류를 감지합니다. 릴리스 빌드에서는 예측할 수 없는 동작, 애니메이션 미실행, UI 미업데이트, 크래시가 발생할 수 있습니다.
전역 큐 대신 수백 개의 사용자 정의 큐를 만드는 것은 안티패턴입니다. 각 큐는 시스템 리소스를 소비합니다. 대부분의 작업에는 다양한 QoS 수준의 전역 동시 큐와 공유 데이터 동기화를 위한 하나 또는 두 개의 직렬 큐로 충분합니다.
autoreleasepool 없이 백그라운드 큐에서 리소스 집약적인 루프 작업을 실행하면 전체 루프가 끝날 때까지 메모리가 증가합니다. ARC는 autorelease 풀을 벗어날 때만 객체를 해제합니다. 적시에 메모리를 해제하려면 루프 반복을 autoreleasepool { }으로 감싸세요.
자주 묻는 질문
OperationQueue는 GCD 위에 구축되었지만 작업 종속성, KVO 및 취소 지원을 갖춘 더 높은 수준의 API를 제공합니다. DispatchQueue는 종속성 관리 없이 간단한 async 작업을 위한 저수준 큐입니다.
GCD는 실행 중인 작업 중지를 지원하지 않습니다. suspend() 메서드는 새 작업만 일시 중지하며 현재 작업은 끝까지 실행됩니다. 취소를 위해서는 작업 코드 내에서 수동으로 플래그를 확인해야 합니다.
즉시 결과 표시가 필요한 주요 요청에는 — .userInitiated. 데이터 사전 로드에는 — .utility. 백그라운드 동기화에는 — .background.
GCD는 스레드 수를 고정하지 않습니다. 스레드 풀은 부하에 따라 동적으로 조정되며 CPU 코어, 현재 부하 및 각 작업의 QoS를 고려합니다. 최대 수는 시스템에 의해 제한됩니다.
UIKit은 스레드 안전하지 않습니다 — 모든 클래스는 메인 스레드에서만 호출되어야 합니다. 위반 시 예측할 수 없는 동작, 업데이트 누락 및 프로덕션에서 크래시가 발생합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.