비동기 프로그래밍의 이해

iosSwift
avatar
2025.02.19
·
5 min read

비동기 프로그래밍이란?

동기(Synchronous) vs 비동기(Asynchronous) 프로그래밍

동기(Synchronous) 프로그래밍

  • 하나의 작업이 끝날 때까지 다음 작업을 실행하지 않음

  • 작업이 완료될 때까지 앱이 멈춘 것처럼 보일 수 있음

print("작업 시작")
sleep(3) // 3초 동안 멈춤
print("작업 완료")

문제점

  • sleep(3) 동안 다른 작업을 할 수 없음

  • 네트워크 요청 같은 작업이 오래 걸리면 UI가 멈출 수 있음

비동기(Asynchronous) 프로그래밍

  • 작업을 백그라운드에서 실행하고 완료될 때까지 기다릴 필요 없음

  • UI가 멈추지 않고 사용자 경험이 개선됨

print("작업 시작")

DispatchQueue.global().async {
    sleep(3)
    print("작업 완료")
}

print("다른 작업 실행 가능!")

// 작업 시작
// 다른 작업 실행 가능!
// (3초 후) 작업 완료

Swift에서 비동기 처리하는 방법

GCD(Grand Central Dispatch) – DispatchQueue 사용

iOS에서는 GCD를 사용해 멀티스레딩을 쉽게 구현할 수 있다.

DispatchQueue.global(qos: .background).async {
    // 백그라운드에서 실행 (예: 네트워크 요청)
    print("백그라운드 작업 실행")

    DispatchQueue.main.async {
        // UI 업데이트 (메인 스레드에서 실행)
        print("UI 업데이트")
    }
}

사용되는 큐의 종류

  1. DispatchQueue.global(qos: .background) → 백그라운드 작업

  2. DispatchQueue.global(qos: .userInitiated) → 사용자 요청 처리

  3. DispatchQueue.main → UI 업데이트

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    print("2초 후 실행됨")
}

OperationQueue 사용

GCD보다 더 객체지향적으로 비동기 작업을 처리할 수 있다.

let queue = OperationQueue()

queue.addOperation {
    print("백그라운드 작업 실행")
    
    OperationQueue.main.addOperation {
        print("UI 업데이트")
    }
}

GCD보다 유연한 작업 제어 가능

  • 작업 간 의존성 설정 가능

  • 특정 작업을 취소하거나 일시 중지 가능

async/await 사용 (Swift 5.5 이상)

비동기 코드의 가독성을 높이는 최신 문법

func fetchData() async {
    print("데이터 요청 시작")
    try? await Task.sleep(nanoseconds: 2_000_000_000) // 2초 대기
    print("데이터 받아옴")
}

Task {
    await fetchData()
}

비동기 코드를 동기 코드처럼 읽기 쉽게 작성 가능

네트워크 요청에서 비동기 프로그래밍 활용하기

전통적인 URLSession 방식

콜백 방식이라 코드가 복잡해질 수 있다.

let url = URL(string: "https://api.example.com/data")!

URLSession.shared.dataTask(with: url) { data, response, error in
    if let data = data {
        print("데이터 크기: \(data.count) bytes")
    }
}.resume()

async/await을 활용한 네트워크 요청 (Swift 5.5 이상)

코드가 간결하고 가독성이 좋다.

func fetchData() async throws -> Data {
    let url = URL(string: "https://api.example.com/data")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

Task {
    do {
        let data = try await fetchData()
        print("데이터 크기: \(data.count) bytes")
    } catch {
        print("에러 발생: \(error)")
    }
}

비동기 프로그래밍 사용 시 주의점

UI 업데이트는 항상 메인 스레드에서 실행해야 함

백그라운드 작업이 끝난 후 UI를 변경하려면 DispatchQueue.main.async를 사용

DispatchQueue.global().async {
    let data = fetchData()
    
    DispatchQueue.main.async {
        label.text = "데이터 업데이트 완료"
    }
}

네트워크 요청 시 에러 처리를 반드시 추가해야 함

do {
    let data = try await fetchData()
    print("데이터 로드 성공")
} catch {
    print("네트워크 요청 실패: \(error)")
}

메모리 관리 주의 – 강한 참조 순환 방지

weak self를 사용해 메무리 누수 방지

class ViewController: UIViewController {
    func loadData() {
        Task { [weak self] in
            guard let self = self else { return }
            let data = try await fetchData()
            print("데이터: \(data)")
        }
    }
}






- 컬렉션 아티클