BIBI BLOG

Swift 6의 새로운 동시성(Concurrency) 기능 이해하기 🚀 본문

iOS/Swift

Swift 6의 새로운 동시성(Concurrency) 기능 이해하기 🚀

BIBI⭐️ 2025. 2. 24. 13:08
728x90

Swift 6의 새로운 동시성(Concurrency) 기능 이해하기 🚀

Swift 6에서는 동시성 관련 기능이 더욱 강화되었습니다! 이제 UI 업데이트를 더 안전하게 관리하고, 데이터 경쟁 상태(Data Race)를 방지하며, 코드의 안정성을 높일 수 있습니다. 하지만 기존 코드를 Swift 6에 맞게 업데이트하려면 몇 가지 변경이 필요합니다.

이 글에서는 Swift 6의 새로운 동시성 기능기존 코드에서 발생할 수 있는 경고 및 해결 방법을 쉽고 자세히 설명하겠습니다. 😊


1. Swift 6 동시성의 주요 변화

Swift 6에서는 코드를 더욱 안전하게 실행하기 위해 동시성 검사를 엄격하게 강화했습니다.
아래와 같은 주요 변화가 있습니다.

UI 업데이트를 더 엄격하게 관리 (@MainActor 적용 필수)
함수와 속성에서 명확한 동시성 선언 필요 (예: @MainActor, @Sendable)
글로벌 상태(Global State) 관리 개선 (@globalActor 활용 가능)
경고 메시지 개선 (스레드 안전하지 않은 코드 감지 가능)


2. Xcode 16으로 업그레이드할 때 발생하는 문제와 해결법

📌 예제 1: UI 업데이트 시 @MainActor를 명시적으로 사용해야 함

🚨 문제:
@MainActor를 사용하지 않고 UI를 업데이트하면, Swift 6에서는 경고가 발생합니다.

수정 전 (경고 발생 코드)

class ProfileViewModel: ObservableObject {
    @Published var title: String = ""

    func loadData() async {
        let data = await fetchData()
        title = data.title // ❌ 경고: Main 스레드에서 실행되지 않음!
    }
}

수정 후 (@MainActor 추가)

@MainActor // ✅ ViewModel 전체를 MainActor에서 실행되도록 지정
class ProfileViewModel: ObservableObject {
    @Published var title: String = ""

    func loadData() async {
        let data = await fetchData()
        title = data.title // ✅ 이제 안전한 UI 업데이트 가능!
    }
}
📝 설명:
  • @MainActor를 클래스에 추가하면, 해당 클래스의 모든 코드가 메인 스레드에서 실행됨.
  • UI 업데이트는 항상 메인 스레드에서 실행해야 안전하므로 Swift 6에서는 이를 강제함.

📌 예제 2: DispatchQueue 대신 MainActor.run 사용하기

Swift 5에서는 UI 업데이트를 위해 DispatchQueue.main.async를 사용했어요. 하지만, Swift 6에서는 더 안전한 MainActor.run을 사용해야 해요.

수정 전 (DispatchQueue 사용)

func updateUI() {
    DispatchQueue.main.async { // ❌ 경고: DispatchQueue 대신 MainActor.run을 사용할 것
        self.label.text = "안녕하세요!"
    }
}

수정 후 (MainActor.run 사용)

func updateUI() async {
    await MainActor.run { // ✅ MainActor.run을 사용하여 UI 업데이트
        self.label.text = "안녕하세요!"
    }
}

📝 설명:

  • DispatchQueue.main.async를 사용하면 메인 스레드에서 실행됨을 보장할 수 없음.
  • await MainActor.run을 사용하면, Swift가 메인 스레드에서 실행됨을 보장하여 더 안전함.

📌 예제 3: 여러 스레드에서 공유되는 데이터 보호하기 (UserDefaults 예제)

공유 상태를 여러 스레드에서 동시에 접근하면 데이터 충돌이 발생할 수 있어요.

수정 전 (경고 발생)

class SettingsManager {
    static let shared = SettingsManager()
    var theme: String = "light" // ❌ 경고: 다중 스레드에서 충돌 가능
}

수정 후 (Global Actor로 보호)

@globalActor
struct SettingsActor {
    actor ActorType { }
    static let shared = ActorType()
}

@SettingsActor // ✅ Custom Global Actor로 보호
class SettingsManager {
    static let shared = SettingsManager()
    var theme: String = "light" // ✅ 이제 안전한 데이터 관리 가능!
}

📝 설명:

  • @globalActor를 사용하면 이 클래스가 특정 스레드에서만 실행되도록 강제할 수 있음.
  • 이를 통해 데이터 충돌(Data Race) 문제를 해결할 수 있음.

3. 가장 많이 발생하는 경고와 해결법

Swift 6에서는 동시성과 관련된 새로운 경고가 많이 발생합니다. 대표적인 사례와 해결법을 알아볼게요.

🚨 경고 1: Non-Sendable 타입을 @MainActor에서 사용하려고 할 때

경고 메시지:

"Non-sendable type 'ViewController' passed in implicitly asynchronous call to main actor-isolated method"

해결 방법:

class ViewController: UIViewController {
    func onButtonTap() {
        Task { @MainActor in // ✅ MainActor에서 실행
            self.updateUI()
        }
    }
    
    @MainActor
    func updateUI() { ... }
}

🚨 경고 2: @Sendable 클로저에서 self를 참조할 때

경고 메시지:

"Capture of 'self' with non-sendable type 'MyClass' in a @Sendable closure"

해결 방법 (weak self 사용)

Task { [weak self] in // ✅ weak self로 참조하여 메모리 누수 방지
    guard let self else { return }
    await self.fetchData()
}

🚨 경고 3: Non-Sendable 타입을 동시성 컨텍스트에서 사용하려고 할 때

경고 메시지:

"Function cannot be marked as '@Sendable' due to non-sendable parameter of type 'DataModel'"

해결 방법 (Sendable 프로토콜 적용)

struct DataModel: Sendable { // ✅ Sendable 적용
    let id: UUID
    let value: String
}

📝 설명:

  • Sendable을 적용하면, 데이터 모델이 여러 스레드에서 안전하게 공유될 수 있도록 보장함.

4. Swift 6에서 동시성을 디버깅하는 방법

Xcode 16에서는 동시성 문제를 더 쉽게 찾을 수 있도록 강력한 디버깅 도구를 제공합니다.

1. Thread Sanitizer(TSan) 활성화하기

데이터 경쟁(Data Race) 문제를 찾으려면?
🔹 Xcode 메뉴에서 Product > Scheme > Edit Scheme > Run > Diagnostics > Thread Sanitizer 활성화

2. Task Tracing 활용하기

비동기 작업이 어떻게 실행되는지 추적하려면?

await withTaskTracing {
    await fetchUserData()
}

3. -strict-concurrency 컴파일러 플래그 활성화

Swift 6의 엄격한 동시성 검사를 사용하려면?
🔹 Build Settings > SWIFT_STRICT_CONCURRENCY = complete 설정


Swift 6 동시성을 쉽게 적용하는 방법

UI 업데이트는 @MainActor를 적극적으로 사용
DispatchQueue 대신 MainActor.run 활용
공유 데이터는 @globalActor로 보호
동시성 오류는 Thread Sanitizer로 검사
Sendable을 점진적으로 도입하여 최적화

 

자료
Understanding the new swift 6 concurrency features

728x90
Comments