Swift 6.3 마이그레이션 오류는 모든 타깃을 한 번에 Swift 6 언어 모드로 바꾸지 말고, 기존 기준을 고정한 뒤 타깃별로 동시성 진단을 처리하는 방식이 가장 안전합니다. Swift 6.3 컴파일러를 쓰는 것과 Swift 6 언어 모드를 활성화하는 것은 별개의 설정이므로, 먼저 범위를 분리해야 합니다.
이 글은 Swift Concurrency 진단이 갑자기 늘어난 iOS·macOS 개발자, 여러 모듈과 혼합 언어를 관리하는 기술 책임자, CI에서 Swift 6.3 결과를 검증해야 하는 빌드 엔지니어를 대상으로 합니다. 단일 앱의 작은 실험보다 오래된 의존성과 다중 타깃이 있는 팀 프로젝트에 초점을 맞춥니다.
마지막 업데이트: 2026년 8월 24일. Swift 6.3 릴리스 상태와 마이그레이션 규칙은 Swift 공식 릴리스 안내와 공식 마이그레이션 문서를 기준으로 확인했습니다.
먼저 컴파일러와 언어 모드를 분리해 기록합니다
Swift 6.3 컴파일러는 빌드 도구입니다. 반면 Swift language mode는 각 타깃이 어떤 Swift 언어 규칙과 동시성 검사를 적용할지 결정하는 설정입니다. 컴파일러를 업그레이드했다고 모든 앱, 프레임워크, 테스트 타깃이 자동으로 Swift 6 언어 모드가 되지는 않습니다. Swift 버전 호환 규칙은 Swift 호환성 문서에서도 확인할 수 있습니다.
이 구분을 하지 않으면 다음 문제가 생깁니다.
- 실제로 전환되지 않은 타깃의 오류를 Swift 6.3 결함으로 오해합니다.
- 기존 경고와 새 동시성 진단을 구분하지 못합니다.
- 의존성 문제를 애플리케이션 코드에 임시 수정으로 전파합니다.
- 빌드 성공만 확인하고 테스트, 코드 서명, 배포 산출물 변화를 놓칩니다.
첫 브랜치를 만들기 전에 타깃별 컴파일러 버전, Swift language mode, 경고 설정, 의존성 잠금 상태, 테스트 결과를 저장하십시오. 오류 개수는 프로젝트마다 다르므로 다른 팀의 수치와 비교하지 말고, 같은 프로젝트의 이전 기준과 비교해야 합니다.
첫 단계: 기준선을 고정하고 이전 범위를 작게 잡습니다
처음부터 저장소 전체를 바꾸지 말고 다음 순서로 준비합니다.
- 현재 배포 브랜치에서 깨끗한 빌드를 실행하고 테스트, 아카이브, 서명 산출물을 보관합니다.
- 프로젝트 설정 파일과 패키지 잠금 파일을 별도 커밋으로 고정합니다.
- 타깃마다 Swift compiler version과 Swift language mode를 표로 기록합니다.
- 의존성이 적고 테스트가 완전한 도구 또는 프레임워크 타깃을 후보로 정합니다.
- 후보 타깃 하나만 Swift 6 언어 모드로 바꾸고, 변경 전후의 진단을 분류합니다.
- 해당 타깃의 공개 API와 상위 타깃을 검증한 뒤 다음 모듈로 이동합니다.
| 점검 대상 | 기존 상태에서 기록할 내용 | 이전 후 통과 조건 |
|---|---|---|
| 언어 설정 | 타깃별 Swift language mode와 경고 수준 | 의도한 타깃만 새 모드로 빌드 |
| 의존성 | 소스 패키지, 바이너리 모듈, 잠금 버전 | 호환 릴리스와 재현 가능한 잠금 |
| 테스트 | 단위, 통합, 비동기 테스트 결과 | 기존 핵심 시나리오와 회귀 테스트 통과 |
| 배포 | 아카이브와 코드 서명 산출물 | 배포용 서명 및 실행 검증 완료 |
두 번째 단계: 동시성 진단을 위험 유형별로 나눕니다
Swift 6.3 마이그레이션 오류를 메시지 이름만 보고 고치면 코드가 조용히 위험해질 수 있습니다. 먼저 진단이 어느 경계에서 발생했는지 확인하십시오.
공유 변경 상태
여러 작업이 같은 배열, 캐시, 세션 객체를 변경한다면 단순히 경고를 숨기는 것보다 소유권을 정해야 합니다. 상태를 하나의 Actor가 관리할지, 호출부에서 값을 복사할지, 직렬 접근이 필요한지를 결정해야 합니다. Apple의 Actor 설명은 Actor가 격리된 변경 상태를 보호하는 기본 동작을 설명합니다.
Actor 격리
동기 함수에서 Actor의 상태를 직접 읽거나, 격리된 인스턴스를 비동기 경계 밖으로 전달하는 코드는 호출 구조를 바꿔야 할 수 있습니다. 이때 await를 기계적으로 추가하지 말고, 호출자가 비동기 함수가 되어도 되는지부터 확인해야 합니다. UI 타깃이라면 메인 실행 영역과 백그라운드 작업의 책임도 분리하십시오.
Sendable과 비동기 경계
클로저, 작업 객체, 대리자 콜백이 다른 실행 영역으로 이동하는지 확인합니다. 참조 타입을 무조건 Sendable로 표시하면 데이터 경쟁을 감출 수 있습니다. Swift 공식 동시성 이전 안내처럼 점진적 채택을 적용하고, 위험한 표시보다 값 타입 변환과 소유권 정리를 우선하십시오.
실무에서는 다음 순서가 효율적입니다.
- 공유 변경 상태를 찾고 소유자를 정합니다.
- Actor 경계에서 동기 호출과 비동기 호출을 구분합니다.
- Sendable 진단이 실제 객체 공유를 뜻하는지 확인합니다.
- 테스트에서 동시에 실행되는 경로를 재현합니다.
- 마지막에만 제한적인 예외 표시를 검토합니다.
Swift Concurrency의 실행 모델과 이전 원칙을 다룬 Apple 세션도 함께 확인하면 진단을 코드 구조와 연결하기 쉽습니다.
세 번째 단계: 오래된 의존성을 경계 모듈에 가둡니다
서드파티 의존성이 Swift 6.3을 지원하지 않는다면 애플리케이션 전체에 임시 수정 코드를 넣지 마십시오. 먼저 공식 저장소에서 호환 릴리스, 소스 수준 수정, 알려진 바이너리 모듈 제약을 확인해야 합니다.
| 의존성 상황 | 우선 대응 | 피해야 할 대응 |
|---|---|---|
| 호환 릴리스가 있음 | 잠금 파일을 갱신하고 독립 타깃에서 검증 | 전체 저장소를 먼저 갱신 |
| 소스 수정이 필요함 | 포크 또는 어댑터 모듈에서 제한적으로 수정 | 호출부마다 예외 처리 복사 |
| 바이너리 모듈만 제공됨 | 모듈 호환성과 배포 환경을 확인 | 빌드 성공만으로 배포 가능 판단 |
| 유지보수가 중단됨 | 대체 구현의 API와 테스트 범위 평가 | 위험한 표시로 진단을 일괄 숨김 |
바이너리 모듈은 소스 패키지와 다르게 현재 컴파일 환경, 모듈 안정성, 배포 대상의 조합에 영향을 받습니다. 따라서 의존성 오류를 해결했다는 판단은 라이브러리 빌드 성공이 아니라 앱 테스트와 아카이브 검증까지 끝난 뒤 내려야 합니다.
네 번째 단계: 다중 타깃과 혼합 언어 경계를 검증합니다
큰 프로젝트에서는 앱 타깃, 프레임워크 타깃, 테스트 타깃, 명령줄 도구가 서로 다른 언어 모드를 가질 수 있습니다. 의존성이 적은 타깃에서 시작해 핵심 비즈니스 모듈로 이동하고, 마지막에 최상위 앱 타깃을 전환하는 편이 변경 범위를 통제하기 좋습니다.
Swift와 Objective-C가 섞인 경우에는 다음 경계를 따로 확인해야 합니다.
- Objective-C 헤더로 노출되는 Swift API의 이름과 nullability
- C 콜백이 Swift 비동기 코드로 들어오는 방식
- Sendable로 판단할 수 없는 포인터, 핸들, 대리자 객체의 소유권
- 브리징 헤더 변경 뒤 기존 테스트와 아카이브 결과
- 모듈 간 공개 접근 수준과 생성 인터페이스
혼합 언어 경계에서 오류가 발생하면 호출부 전체를 새 언어 모드에 맞추기보다, 어댑터를 둬서 비동기 경계와 데이터 전달 규칙을 한 곳에 모으십시오. 이렇게 해야 다음 타깃으로 이전할 때 같은 수정이 반복되지 않습니다.
다섯 번째 단계: CI에서 두 경로를 동시에 검증합니다
Swift 6.3 마이그레이션 실패가 현재 출시를 막지 않게 하려면 안정 브랜치와 이전 브랜치를 분리해야 합니다. 기존 언어 모드의 안정 빌드는 계속 유지하고, 새 모드는 별도 빌드 노드에서 실행하십시오. 빌드 노드 격리 운영 안내를 참고하면 출시용 환경과 실험용 환경의 접근 범위를 나누는 기준을 세우는 데 도움이 됩니다.
두 파이프라인은 최소한 다음 결과를 각각 보관해야 합니다.
- 컴파일 및 경고 진단
- 단위, 통합, 동시성 회귀 테스트
- 아카이브와 실행 확인
- 코드 서명 및 배포 산출물
- 의존성 잠금 파일과 빌드 스크립트 변경
코드 서명은 빌드 성공과 별개의 검증 대상입니다. 배포용 Mac 코드 서명 절차는 Apple의 서명 안내에 맞춰 확인하십시오.
현재 출시 파이프라인이 새 브랜치의 실패를 받아들이지 못하는 구조라면, 새 검사를 필수 상태로 바로 바꾸지 마십시오. 먼저 결과를 비교하는 선택적 작업으로 운영하고, 테스트와 서명이 안정된 뒤 필수 단계로 승격해야 합니다.
완료 판정은 경고 수가 아니라 복구 가능성까지 봅니다
다음 항목을 모두 만족해야 이전 완료로 판정하십시오.
- [ ] 모든 목표 타깃의 Swift language mode를 문서화했습니다.
- [ ] 실제 데이터 경쟁 가능성이 있는 공유 상태를 소유자별로 정리했습니다.
- [ ] Actor, Sendable, 비동기 호출 경계를 테스트로 검증했습니다.
- [ ] 오래된 의존성의 호환 버전 또는 격리된 어댑터를 확정했습니다.
- [ ] Swift와 Objective-C, C 경계의 공개 API를 확인했습니다.
- [ ] 안정 빌드와 Swift 6.3 이전 빌드가 CI에서 병렬 검증됩니다.
- [ ] 테스트, 아카이브, 코드 서명, 실행 결과를 모두 비교했습니다.
- [ ] 이전 언어 모드와 의존성 잠금 상태로 돌아가는 롤백을 실제로 연습했습니다.
특히 마지막 항목이 빠지면 마이그레이션은 완료된 것이 아닙니다. 새 모드에서 테스트가 통과해도 특정 배포 환경이나 서명 단계에서 실패할 수 있기 때문입니다. 모든 목표 환경이 통과하기 전에는 기존 도구 체인 노드를 제거하지 않는 편이 안전합니다.
현재 빌드 환경과 독립 Mac 환경을 비교할 때의 판단
한 대의 Mac 빌드 노드에서 컴파일러, 언어 모드, 의존성, 서명 설정을 동시에 바꾸면 실패 원인을 분리하기 어렵습니다. 현재 환경을 계속 사용하는 방식은 장기적으로 단순하지만, 출시 브랜치와 실험 브랜치가 같은 파일과 키체인을 공유하고, 롤백 때 설정을 되돌리는 부담이 커진다는 단점이 있습니다.
반대로 독립된 Mac 환경을 사용하면 Target별 이전과 병렬 CI를 분리하기 쉽고, 기존 배포 흐름을 건드리지 않고 새 의존성 및 서명 상태를 검증할 수 있습니다. 다만 장기간 고정된 대규모 빌드나 물리 장치 직접 연결이 필수인 작업에는 임시 환경보다 자체 장비가 더 적합할 수 있습니다.
팀에 지금 필요한 것이 단기적인 Swift 6.3 검증, 새 언어 모드 실험, 격리된 CI 노드라면 Macstripe의 구성 주문 안내를 기준으로 필요한 환경을 먼저 확인해 볼 수 있습니다. 기존 출시 노드를 보존하면서 별도 Mac 환경을 확보하는 방식이, 전역 설정을 한 번에 바꾸는 것보다 실패 범위와 복구 시간을 관리하기 쉽습니다.