Swift Sendable: iOS 개발에서 스레드 안정성 주도하기

카테고리

프로그래밍/소프트웨어 개발

서브카테고리

개발 툴

대상자

  • iOS 개발자
  • 중급~고급 수준 (Swift 병렬 프로그래밍 개념 이해 필요)

핵심 요약

  • Sendable 프로토콜은 스레드 간 안전한 데이터 공유를 보장하는 Swift의 핵심 기능
  • 값 타입(struct/enum)은 불변성(immutable)을 유지하면 자동 Sendable
  • 참조 타입(class)은 불변성 또는 (NSLock)으로 스레드 안정성 확보 필요
  • Actor는 내장 스레드 안전성 제공, 자동 Sendable

섹션별 세부 요약

1. Sendable의 목적과 중요성

  • 데이터 레이스(Data Race) 방지: 병렬 작업에서 items, totalPrice 동시 수정으로 인한 메모리 손상, 불확실한 계산 방지
  • Sendable스레드 간 안전한 데이터 전달을 보장하는 Swift의 철학
  • Sendable 미구현 시 런타임 크래시 발생 가능성

2. 값 타입의 Sendable 구현

  • 불변성(immutable)을 가지는 struct/enum은 자동 Sendable
  • struct User: Sendable {
        let id: UUID
        let name: String
    }
  • 모든 속성이 Sendable인 경우만 Sendable 프로토콜 준수 가능
  • 예시: String, Int, Array, Dictionary

3. 참조 타입의 Sendable 구현

  • 불변성으로 전환:
  • final class Configuration: Sendable {
        let apiKey: String
        let baseURL: URL
    }
  • (NSLock) 활용:
  • final class AtomicCounter: Sendable {
        private let lock = NSLock()
        private var _value: Int = 0
        func increment() { lock.withLock { _value += 1 } }
    }

4. Actor로 스레드 안정성 확보

  • Actor는 내장 스레드 안전성 제공, 자동 Sendable
  • actor DataCache {
        private var cache: [String: Any] = [:]
        func store(key: String, value: Any) { cache[key] = value }
    }

5. Sendable 사용 시 주의사항

  • 변경 가능한 속성(mutable)은 Sendable 준수 불가
  • 고차함수에서 @Sendable 명시:
  • func executeAsync(operation: @Sendable @escaping () async throws -> Void) {
        Task { try await operation() }
    }
  • 제네릭 타입Sendable 제약 추가:
  • func process(_ items: [T]) async { ... }

결론

  • 스레드 안정성을 위해 Sendable 준수 타입 사용, 불변성과 Actor 활용
  • 값 타입은 불변성 유지, 참조 타입은 락 또는 불변성으로 처리
  • 제네릭 함수고차함수에서 Sendable 제약 명시 필수
  • Actor를 사용해 복잡한 상태 관리 시 스레드 안전성 보장