study record

[Swift Concurrency] Strict Concurrency Checking 본문

Swift/Concurrency

[Swift Concurrency] Strict Concurrency Checking

asong 2025. 4. 28. 21:30

Introduction


Swift Concurrency란?

  • Swift 5.5부터 도입된 비동기 프로그래밍 모델
  • async/await, Task, actor 등을 통해 동시성 프로그래밍을 쉽게, 그리고 안전하게 작성할 수 있도록 지원
  • 목적: 성능 향상 + 코드 안전성 강화(Data Race 제거) 

핵심 기법: Sendable checking 및 actor 기반 상태 격리

 

Problem

기존 코드와의 호환성 문제

  • Concurrency 도입 전, Swift 생태계에는 이미 수많은 코드가 존재

이러한 기존 코드는:

  • Sendable, @MainActor 같은 동시성 어노테이션이 없음
  • 동시성 도입 시 오류가 발생하거나 빌드가 막힘

Sendable, @MainActor 같은 동시성 어노테이션 누락에 따른 오류나 진단은

Swift 5에서는 대부분 무시되거나 경고 수준이고,

Swift 6에서는 "엄격한 동시성 검사(strict concurrency checking)"가 기본이라 오류로 바뀌게 된다.

 

 

Strict Concurrency checking

  • Swift 6에서 기본이 되는 모드로, 동시성 오류를 컴파일 타임에 오류로 잡는 모드
  • Sendable, @MainActor, @Sendable 누락 또는 잘못된 사용 검사
  • 동시성 버그를 미리 방지해서 앱 안정성 향상



Concurrency checking mode

1) Strict concurrency checking

  • Sendable 채택 누락이나 글로벌 액터 어노테이션 누락이 확인
  • Swift 6에서는 대부분 오류(error)로 처리, actor, async

2) Minimal concurrency checking

  • Sendable 누락이나 글로벌 액터 어노테이션 누락이 경고(warning)로 처리
  • 주로 경고 또는 무시

 

하위 스코프의 검사 모드 결정 기준

부모 스코프가 최소 모드(Minimal)이고, 다음 조건 중 하나라도 해당되면 자식 스코프는 엄격 모드(Strict)를 갖습니다:

  • 명시적인 글로벌 액터 속성이 붙은 클로저
  • async 또는 @Sendable 타입의 클로저 혹은 자동 클로저
    (단, 클로저 타입 추론에 부모 스코프의 모드가 영향을 줄 수 있음)
  • nonisolated 또는 글로벌 액터 속성이 명시된 선언
  • async 또는 @Sendable이 명시된 함수, 메서드, 이니셜라이저, 접근자, 변수, 서브스크립트
  • actor 선언

이외의 경우에는 부모 스코프와 동일한 모드를 사용합니다.

 

 

Swift/Xcode settings for concurrency checking

  • Swift Language Version을 Swift 6로 변경
  • 6로 변경하기 전 Build Setting 옵션 중 Strict Concurrency Checking을 활성화
  • Swift 5.x에서 -warn-concurrency 컴파일 플래그를 사용

✅ 동시성 환경에서 Data Race가 발생할 가능성이 있는 곳에서 에러 또는 경고를 표시

✅ 프로젝트를 Swift 6로 올리기 전에 사전 점검!

 

 

Strict Concurrency checking

swift 5



swift 6

 

 

 

Problem 1

@Sendable 클로저에서 비-Sendable 타입을 캡처하면 컴파일 오류

 

swift 5

 

swift 6

 

 

Problem Solving

@unchecked 활용 또는 actor 사용

  • unchecked는 개발자가 데이터 레이스로 부터 안전한지 직접 검증해야 함
  • actor가 swift가 선호하는 의도하는 방향으로 생각됨.

 

Problem 2

 

swift 5

 

swift 6

Swift 6부터는 함수 파라미터가 @Sendable이면 기본적으로 @escaping으로 취급

 

 

 

Problem Solving

점진적인 migration을 위한 도구

  • @unchecked Sendable: 위험을 감수하고 Sendable 수동 선언
  • @preconcurrency: Swift Concurrency를 기존 코드에 점진적으로 적용할 수 있도록 설계

 

@preconcurrency

  • Swift 6의 엄격한 동시성 검사에서, 기존 코드가 갑자기 에러 나지 않도록 보호해주는 호환성 속성
  • "pre-concurrency" → 동시성 기능 도입 전에 작성된 코드라는 뜻
  • 이 속성을 붙이면, Swift 6의 엄격한 검사(strict concurrency checking)에서 일부 오류를 경고로 완화하거나 무시하게 함
  • Swift Concurrency를 기존 코드에 점진적으로 적용할 수 있도록 설계

 

선언에 적용된 @preconcurrency의 의미

  • 어떤 타입(예: 클래스, 구조체, 프로토콜 등)의 선언에 @preconcurrency를 적용하면,
    해당 선언이 동시성 지원을 위해 바뀌었지만, Swift 5에서는 기존처럼 느슨하게 취급하라는 의미

 

import 문에 적용된 @preconcurrency의 의미

  • import 모듈명 앞에 @preconcurrency를 붙이면,
    그 모듈 안의 타입이 Sendable, @MainActor 등을 제대로 지정하지 않았더라도 엄격한 동시성 오류를 완화(경고 또는 무시)해준다.
  • 단, 그 타입이 명시적으로 Sendable을 선언했으나 사용 조건을 만족하지 못한 경우,
    경고(warning)는 발생할 수 있다.
  • 그러나 이 경우에도 Swift 6 모드에서 오류가 아닌 경고 수준으로 판단된다.
    • Add @preconcurrency to suppress Sendable-related warnings from module MyModule

 

 

Sendable 적합성 작동 기준

1. 암묵적으로 Non-Sendable 타입이 필요한 곳에서 사용될 경우:

  • @preconcurrency import를 통해 임포트된 타입이면:
  • 그렇지 않으면 일반적인 오류 발생 + @preconcurrency import 사용 권장 메시지 출력

2. 명시적으로 Non-Sendable 타입이 필요한 곳에서 사용될 경우:

  • @preconcurrency import를 통해 임포트된 타입이면:
  • 그렇지 않으면 일반적인 오류 발생

3. @preconcurrency가 더 이상 필요 없게 된 경우:

  • 해당 속성이 사용되지 않았다고 판단되면, 이를 제거하라는 경고가 출력됨
    (단, 어떤 경우가 "사용되지 않음"인지에 대한 구체적인 기준은 아직 정의되지 않음)

 

@preconcurrency가 붙은 Sendable 프로토콜

기존에 존재하는 많은 프로토콜은 실질적으로 Sendable 타입을 요구하지만, 초기에는 이를 명시할 수 없었다. 

이런 프로토콜이 동시성에 맞게 업데이트되면, 대개 Sendable을 상속하게 된다.

하지만 이렇게 하면, 해당 프로토콜을 채택하고 있던 기존 타입들이 자동으로 Sendable로 간주되면서 오류가 발생할 수 있다.

 

ErrorCodingKey 프로토콜: 기존 타입들이 오류 대신 경고로 진단

@preconcurrency protocol Error: Sendable

@preconcurrency protocol CodingKey: Sendable

  > The Error and CodingKey protocols will adopt Sendable in a @preconcurrency context so that existing conformers do not immediately break in Swift 6.



Swift Concurrency Migration Strategy

  • Swift 6는 강력한 동시성 보장, 그러나 호환성 이슈 존재
  • @preconcurrency를 통해 기존 코드와의 안정적 연결 유지
    • 오류/경고 발생 → 다른 모듈의 타입이면 @preconcurrency import로 임시 억제
    • 외부 모듈이 Sendable 도입 시 경고 발생 → 버그 수정
    • @preconcurrency가 불필요해지면 경고 → 제거
  • 점진적인 Swift 6 대응 및 Sendable 검사 도입 지원
  • 점진적 마이그레이션으로 실질적인 생산성 유지 + 미래 호환성 확보

 

 

 

참고

SE-0337 Incremental migration to concurrency checking

https://developer.apple.com/videos/play/wwdc2022/110351/