Swift 5.5부터 도입된 Swift Concurrency는 기존 GCD나 Combine을 대체하며, 비동기 프로그래밍을 더 직관적이고 안전하게 만들었습니다. 이 글에서는 Swift Concurrency의 핵심 개념과 사용법을 간단하게 정리합니다.
1. Swift Concurrency 개요
Swift Concurrency는 async/await, Task, actor 등을 통해 동시성 프로그래밍을 단순화하는 기능입니다. 이를 사용하면 콜백 지옥을 방지하고, 가독성이 높은 코드를 작성할 수 있습니다.
기존의 GCD를 사용한 코드 예제:
DispatchQueue.global().async {
fetchData { result in
DispatchQueue.main.async {
updateUI(with: result)
}
}
}
Swift Concurrency를 활용한 코드 예제:
func loadData() async {
let result = await fetchData()
updateUI(with: result)
}
이처럼 async/await을 활용하면 동기 코드처럼 작성할 수 있어 가독성이 좋아집니다.
2. async/await의 기본 사용법
async 함수 선언
async 함수는 비동기적으로 실행될 수 있도록 선언됩니다.
func fetchUserData() async -> String {
return "User Data"
}
await을 활용한 호출
비동기 함수는 await을 사용하여 호출해야 합니다.
func getUserInfo() async {
let userData = await fetchUserData()
print(userData)
}
Task를 이용한 비동기 실행
비동기 코드는 Task를 사용해 실행할 수도 있습니다.
Task {
await getUserInfo()
}
Task 내부에서 await을 사용하면 동기 코드처럼 실행할 수 있습니다.
3. Task와 Task Group
Task 사용법
Task는 백그라운드에서 비동기 작업을 실행하는 구조입니다.
Task {
let data = await fetchUserData()
print(data)
}
Task의 우선순위 지정
우선순위를 지정하여 성능을 최적화할 수 있습니다.
Task(priority: .high) {
let data = await fetchUserData()
print(data)
}
Task Group을 활용한 병렬 처리
TaskGroup을 사용하면 여러 개의 비동기 작업을 동시에 실행할 수 있습니다.
func fetchMultipleUsers() async {
await withTaskGroup(of: String.self) { group in
for _ in 1...5 {
group.addTask {
return await fetchUserData()
}
}
}
}
TaskGroup을 활용하면 여러 개의 비동기 작업을 한 번에 실행하고 결과를 모을 수 있습니다.
4. Actor를 활용한 동시성 문제 해결
기존 동시성 문제
클래스 기반의 멀티스레드 환경에서는 Race Condition이 발생할 수 있습니다.
class Counter {
var value = 0
func increment() {
value += 1
}
}
이 클래스를 여러 개의 스레드에서 동시에 호출하면 value의 상태가 불확실해질 수 있습니다.
actor를 활용한 해결 방법
Swift의 actor는 이러한 동시성 문제를 해결하는 새로운 타입입니다.
actor Counter {
var value = 0
func increment() {
value += 1
}
}
이제 Counter의 increment() 메서드는 한 번에 하나의 호출만 실행되므로 Race Condition이 발생하지 않습니다.
5. async/await과 기존 코드 통합하기
기존 콜백 기반 함수와 통합
기존 콜백 기반의 함수를 async 함수로 변환할 수 있습니다.
func fetchData(completion: @escaping (String) -> Void) {
DispatchQueue.global().async {
completion("Data Loaded")
}
}
이 함수를 async 버전으로 변환하면:
func fetchData() async -> String {
return await withCheckedContinuation { continuation in
fetchData { data in
continuation.resume(returning: data)
}
}
}
이제 await을 사용하여 호출할 수 있습니다.
let data = await fetchData()
print(data)
6. Swift Concurrency 사용 시 주의할 점
1. MainActor를 활용한 UI 업데이트
UI 관련 코드는 반드시 메인 스레드에서 실행해야 합니다.
@MainActor func updateUI() {
// UI 업데이트 코드
}
또는 MainActor 속성을 적용할 수도 있습니다.
@MainActor class ViewModel {
var data: String = ""
}
2. Cancellation(작업 취소)
비동기 작업이 필요하지 않을 경우 취소할 수 있습니다.
let task = Task {
await fetchData()
}
task.cancel()
isCancelled 프로퍼티를 사용하면 현재 작업이 취소되었는지 확인할 수 있습니다.
if Task.isCancelled {
return
}
결론
Swift Concurrency를 활용하면 기존의 GCD 기반 코드보다 더 간결하고 직관적인 비동기 프로그래밍이 가능합니다. 특히 async/await, Task, actor 등을 활용하면 Race Condition을 방지하고, 코드의 가독성을 높일 수 있습니다.
다음과 같은 경우 Swift Concurrency를 고려해볼 수 있습니다.
- 콜백 기반의 비동기 코드를 async/await으로 변환하고 싶을 때
- Race Condition 문제를 actor로 해결하고 싶을 때
- 여러 개의 비동기 작업을 병렬로 실행하고 싶을 때
'클라이언트' 카테고리의 다른 글
[iOS] 앱 성능 최적화 및 메모리 관리: ARC, 메모리 릭, Instruments 관련 (0) | 2025.02.15 |
---|---|
[Swift] Swift6의 주요 내용 (0) | 2025.02.15 |
[SwiftUI] SwiftUI 성능 최적화: View Rendering을 줄이는 방법 (0) | 2025.02.15 |
[SwiftUI] SwiftUI View 렌더링 순서 관련 정리 (0) | 2025.02.15 |
[SwiftUI] SDWebImage SwiftUI로 WebP 이미지 보여주기 (0) | 2025.02.15 |