DevOps / CI·CD 2026년 4월 24일

2026-04-24 임대 클라우드 Mac에서 xcodebuild CI의 Swift 6 엄격 동시성, Sendable 및 @MainActor

MacXCode Engineering Team 2026년 4월 24일 약 19분

HK / JP / KR / SG / US에서 이미 Apple Silicon 호스트를 쓰는 iOS·macOS 빌드 오너는 두 번째 부담에 직면합니다. Swift 6 언어 모델과 엄격 동시성 검사는 따뜻한 노트북과 임대 SSH Mac에서 차갑고 병렬화된 xcodebuild 잡 사이의 “내 머신에선 된다”를 측정 가능한 차이로 바꿉니다. 이 글(2026-04-24)은 언어 튜토리얼이 아니라 빌드 설정(SWIFT_STRICT_CONCURRENCY, Swift 언어 버전), DerivedData·index store 격리, 병렬셀프호스티드 Runner 토폴로지에서 폭발하는 Sendable / nonisolated / MainActor 노이즈의 단계적 이전을 매핑합니다. tmp DerivedData + xcresult 격리원격 아카이브로 교차 링크해 싱가포르의 같은 Node미국 샤드가 사적이라고 가정했던 공유 ModuleCache를 갈가리질하지 않게 하세요.

헤드리스 CI에서 Swift 6 엄격 동시성이 다르게 반응하는 이유

엄격 검사는 시뮬레이터가 절대 밟지 않는 데이터 레이스를 드러냅니다. UI 메인 스레드가 포착한 var 수명을 가리기 때문입니다. CI에서는 병렬 swift-frontendwhole-module 최적화가 진단 순서를 바꿉니다. 64 GB 개발 노트북에서 하룻밤 컴파일되던 같은 패키지증분이 꺼진 -warnings-as-errors 레인에서 실패할 수 있습니다. 엄격 동시성은 이미 다루는 아카이브 bitcode 폐기나 CLT vs 풀 Xcode 패리티처럼, 고정된 DEVELOPER_DIR를 가진 별도 파이프라인 스테이지와 리팩터 전까지 SWIFT_STRICT_CONCURRENCY=targeted로 둘 수 있는 타깃에 대한 서면 정책으로 다루세요.

인덱스 재빌드 시간도 예산에 넣으세요. 깨끗한 DerivedData에서 첫 Swift 6 풀 빌드는 App Intents나 큰 WidgetKit 익스텐션을 포함한 앱에서 ExtractAppIntentsMetadata 등에 몇 분을 더 쓸 수 있습니다. 이 벽시계 점프는 동시성 실패가 아니라 첫 SwiftPM 레지스트리 프리웜과 같은 콜드 캐시 효과로, PM에게도 같은 선에서 미리 공유하세요. 전후 p95를 대시보드에 남겨 “Swift 6 주”가 클라우드 용량 퇴행으로 읽히지 않게 합니다.

빌드 설정: 언어 버전과 엄격 동시성 모드

프로젝트 또는 xcconfig에서 세 손잡이를 맞춥니다. (1) 출하하는 Swift 6 툴체인에 SWIFT_VERSION; (2) 머지 블로킹 레인에서는 SWIFT_STRICT_CONCURRENCYcomplete로, 레거시 벤더 정적 lib는 주석이 달릴 때까지 targeted일 수 있음; (3) Swift 6의 타깃별 기본 격리에서 UIKit·SwiftUI 모듈은 명시적 @MainActor 커버리지를 원합니다. CI YAML에서는 스킴이 아직 못 담을 때만 오버라이드를 넘깁니다. 예: 권고 레인에서 OTHER_SWIFT_FLAGS=-warn-concurrency. 비트 단위 재현이 필요한 릴리스 아카이브에 몰래 섞지 마세요. 패키지 플러그인과 앱 타깃의 차이를 문서화하세요. 플러그인 빌드 로그는 중요한 바이너리에 -strict-concurrency=complete를 켜기 전까지 오류를 숨길 수 있습니다.

xcodebuild 호출: 아카이브 vs 빌드 vs 분석

호스트에서 DEVELOPER_DIR=/Applications/Xcode.app를 하나로 고정하고 명시적 -configuration·-destination으로 스킴 드리프트를 피합니다. UI 테스트 전 컴파일만의 엄격 게이트라면 xcodebuild -scheme App build -destination 'platform=iOS Simulator,name=iPhone 16'CODE_SIGNING_ALLOWED=NO를 붙여 컴파일러 신호만 필요할 때 쓰고—두 번째 잡에서 서명을 되돌린 시뮬레이터 테스트를 돌리며 XCTest + xcresult 가이드와 같은 분할을 취합니다. 최소 엄격 게이트:

DEVELOPER_DIR=/Applications/Xcode.app xcodebuild build -workspace App.xcworkspace -scheme App -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 16' -derivedDataPath "$CI_DERIVED/Swift6Gate" OTHER_SWIFT_FLAGS='$(inherited) -warn-concurrency'

CI 가드레일: 인프라 플레이크에만 재시도를 허용하고 동시성 오류에는 쓰지 말 것—Sendable에서 두 번 연속 동일 실패는 실제 코드 결함이지 Xcode 유령이 아님.

병렬 레인, DerivedData, 인덱스 스토어

부채꼴로 펼칠 때(M4 부채꼴 참조) 자식 잡마다 비공개 -derivedDataPath를 주고 엄격 스테이지에서는 증분을 끄는 것도 검토해 HK / JP / KR / SG / US 빌더 간 진단을 결정적으로 만드세요. NFS류 마운트에서 공유 DerivedData는 재시도하면 사라지는 유령 “cannot assign through subscript: base is not a concurrent value” 오류를 만드는 가장 빠른 길입니다—바로 격리 글이 xcresult에 대해 경고한 플레이크 패턴입니다. 비용 때문에 반드시 공유한다면 최소한 Index/DataStore를 잡마다 $(CI_JOB_ID) 접두 경로로 두세요.

이전 매트릭스: 경고, 오류, 오너

신호 조치 오너
Sendable 클로저 캡처 값 타입으로 리팩터, 타입을 Sendable로 표시하거나 actor로 이동 피처 스쿼드
MainActor 격리 드리프트 UI 업데이트를 작은 퍼사드 타입으로 모음 클라이언트 리드
Sendable 없는 서드파티 바이너리 모듈 경계로 감싸고 벤더 이슈를 고정하거나 포크 빌드 플랫폼

동시성 오류가 아닐 때

일부 실패는 Swift 6 문제로 위장합니다. 프로비저닝·키체인 문제는 CodeSignswift-frontend 줄과 끼어듭니다. dSYM 공백은 심볼리케이션을 깨뜨릴 뿐 컴파일러는 아닙니다. dSYM 위생을 엄격 레인과 같은 주간 리뷰에 넣지 않으면 온콜이 잘못된 diff를 쫓습니다.

서명, 엔타이틀먼트, 같은 빌드의 “엄격”

컴파일러 검사를 늘리는 릴리스 트레인과 프로비저닝 로테이션을 한 번에 넣는 것은 위험합니다. 순서: (1) 일회용 카나리에서 서명이 녹색임을 증명, (2) 엄격 스테이지 활성화, (3) 그다음 기본 스킴에 SWIFT_STRICT_CONCURRENCY=complete를 강제하는 피처 플래그 머지. 같은 SSH Mac mini M4 풀이 둘 다 호스트해도 을 하나의 워크스페이스 폴더에서 끼워 넣지 마세요. 병렬성은 이미 아카이브 문서를 따를 때만 강점입니다.

동시성 실패가 사실은 의존성 그래프 불일치라면 SwiftPM + 레지스트리 캐시Ruby + CocoaPods 결정성에서 시작하세요. Xcode Cloud와 전용 호스트를 섞는 팀은 둘 다에서 언어 플래그를 맞추지 않으면 전용 엄격 레인만 Xcode Cloud가 보지 못한 머지를 거부합니다.

FAQ: 공유 빌더의 Swift 6

질문 실무적 답
먼저 SPM 패키지에 켜야 하나? 종종 그렇다—경계를 매핑한 뒤 앱 타깃. 테스트 전용 코드를 나누면 패키지 trait 사용.
VNC가 필요한가? 보통 아니요—시각 디버깅 브레이크 글래스는 VNC. 엄격 빌드 로그용은 아님.
인덱스용 디스크는? 큰 SwiftUI 프리뷰를 꺼도 큰 Index 트리를 남기면 1–2 TB 호스트를 고르세요. 엄격 레인이 아티팩트 churn을 3×로 만들면 요금 참조.

동시성 부하가 큰 컴파일에 베어 메탈 Mac mini M4가 맞는 이유

엄격 검사는 CPU·I/O 집약적입니다. 컴파일러가 그래프를 다시 쓰고 index는 부풀며 index store는 아카이브용으로 이미 예산한 것과 같은 NVMe에서 이득을 봅니다. 소켓 스택이 시끄러운 이웃 없음이면 레인이 패키지 해석 중 원격 캐시레지스트리와 말할 때 테일 지연도 줄어듭니다. 엄격 파이프라인이 안정되면 공유 DerivedData로 레인을 접기 전에 같은 리전에서 두 번째 Mac mini M4스케일아웃하세요—2026 메인라인은 결정적 신호를 받을 가치가 있고 복권 빌드는 아닙니다.

M4에서 Swift 6 클린 빌드

NVMe · 격리 레인 · 글로벌 리전