Xcode 27 / Swift 6.3 SPM 동적 라이브러리(Dynamic Framework) 링킹 충돌 원인과 해결 방법
Xcode 27 및 Swift 6.3 환경에서 Swift Package Manager(SPM)를 통해 외부 라이브러리를 추가하거나 멀티 모듈 프로젝트를 구성할 때 duplicate symbol, objc-class-ref, 또는 앱 실행 시 dyld: Library not loaded 링킹 에러가 발생하는 사례가 증가하고 있습니다. 이는 메인 앱 타깃과 확장 타깃(Widget, Notification Service Extension 등)이 동적 라이브러리를 중복 링킹하면서 발생하는 구조적 문제입니다.
Static vs Dynamic 링킹 방식 및 Xcode 27 변경점
SPM 패키지의 Package.swift에서 라이브러리 타입을 명시하지 않으면 기본적으로 Static 형태로 빌드됩니다. 그러나 해당 라이브러리를 메인 앱과 앱 익스텐션 타깃 모두에서 참조할 경우 Xcode 27 빌드 시스템에서 링킹 충돌이 발생합니다.
구분 Static Library (.static) Dynamic Framework (.dynamic)
| 바인딩 시점 | 컴파일/빌드 타임에 바이너리에 포함 | 앱 실행 시(Runtime) 동적 로딩 |
| 다중 타깃 참조 시 | 각 타깃 바이너리마다 코드 중복 복사 발생 | 메인 앱 바이너리에 1회 임베드 후 공유 |
| 대표 발생 에러 | Duplicate symbol _OBJC_CLASS_$_... | dyld: Library not loaded / Image Not Found |
| Xcode 27 권장 | 단일 타깃 전용 라이브러리 | 메인 앱 + Extension 공통 참조 라이브러리 |
1. 문제 상황: 다중 타깃 링킹 충돌 에러 패턴
메인 앱 타깃과 위젯 익스텐션 타깃이 동일한 SPM 라이브러리를 각각 링킹하도록 설정되어 있으면 아래와 같은 빌드 및 런타임 오류가 나타납니다.
// 컴파일 타임 에러 예시 (Static 중복 링킹)
error: duplicate symbol '_OBJC_CLASS_$_SharedUtility' in:
/Users/.../Build/Products/Debug-iphonesimulator/MainApp.o
/Users/.../Build/Products/Debug-iphonesimulator/WidgetExtension.o
// 런타임 에러 예시 (Dynamic Embed 설정 오류)
dyld[1234]: Library not loaded: @rpath/SharedFramework.framework/SharedFramework
Referenced from: <UUID> /var/containers/Bundle/Application/.../WidgetExtension
Reason: tried: '/usr/lib/swift/SharedFramework.framework' (no such file)
2. 해결 방안: SPM 및 Xcode 타깃 링킹 재구성
방안 1: Package.swift 라이브러리 타입 명시적 선언
자체 제작한 모듈이거나 사내 SPM 패키지인 경우 Package.swift 내의 products 정의에서 라이브러리 타입을 .dynamic으로 명시합니다.
// Package.swift
import PackageDescription
let package = Package(
name: "SharedCore",
platforms: [.iOS(.v17)],
products: [
// type: .dynamic 옵션을 통해 런타임 동적 라이브러리로 빌드 지정
.library(
name: "SharedCore",
type: .dynamic,
targets: ["SharedCore"]
),
],
targets: [
.target(
name: "SharedCore",
dependencies: []
),
]
)
방안 2: Xcode Build Target의 Embed 설정 최적화
서드파티 오픈소스 SPM 라이브러리(Package.swift 수정이 불가능한 경우)는 Xcode 프로젝트의 Frameworks, Libraries, and Embedded Content 설정을 분리해야 합니다.
- 메인 앱 타깃 (Main App Target):
- SPM 라이브러리를 추가하고 Embed & Sign으로 설정합니다.
- 익스텐션 타깃 (Widget / Extension Target):
- 동일한 SPM 라이브러리를 추가하되 Do Not Embed로 지정합니다.
- 이렇게 설정하면 메인 앱 번들에 동적 프레임워크가 1회 포함되고, 익스텐션은 런타임 시 메인 앱의 @executable_path/Frameworks 또는 @loader_path/Frameworks 경로를 통해 해당 라이브러리를 참조합니다.
3. Build Settings 추가 체크리스트
- Runpath Search Paths (LD_RUNPATH_SEARCH_PATHS) 확인:
- 익스텐션 타깃의 Build Settings에서 Runpath Search Paths에 다음 경로가 포함되어 있는지 확인합니다.
- @executable_path/Frameworks
- @executable_path/../../Frameworks (App Extension의 메인 프레임워크 탐색 경로)
- Other Linker Flags (OTHER_LDFLAGS) 정리:
- 타깃 빌드 옵션에서 불필요하게 명시된 -all_load 또는 -force_load 플래그가 SPM 라이브러리와 중복 링킹을 유발하지 않는지 점검합니다.
핵심 요약
- Xcode 27 / Swift 6.3 환경에서 메인 앱과 App Extension이 동일한 SPM 라이브러리를 참조할 때 Static/Dynamic 링킹 설정 미숙으로 충돌이 발생합니다.
- 메인 앱과 익스텐션에서 동시 참조하는 라이브러리는 Dynamic 형태로 구성해야 합니다.
- 메인 앱 타깃에는 Embed & Sign을 지정하고, 익스텐션 타깃에는 Do Not Embed로 설정하여 런타임 참조 공유 방식을 적용합니다.
#Xcode27 #Swift6_3 #SPM #SwiftPackageManager #DynamicFramework #LinkerError #iOS개발 #Xcode트러블슈팅
'개발 > Swift' 카테고리의 다른 글
| Swift 6 렌더링 최적화: ObservableObject에서 @Observable 마이그레이션 가이드 (0) | 2026.08.20 |
|---|---|
| Swift 6 Strict Concurrency 대응: SwiftData ModelActor 비동기 파싱 및 저장 가이드 (0) | 2026.08.05 |
| Swift 6 데이터 레이스 컴파일 에러 해결: MainActor와 Sendable 실무 패턴 (0) | 2026.07.31 |
| TCA Study - State · Action · Reducer · Store — TCA의 핵심 구조 완전 정복 (0) | 2025.12.16 |
| TCA Study - Swift 개발자가 TCA를 알아야 하는 이유 (0) | 2025.12.16 |
| Swift에서 정렬: sort vs sorted (0) | 2025.11.11 |
| Swift에서 옵셔널 기본값: 중첩 if let vs a ?? b (0) | 2025.11.11 |
| Swift에서 배열 초기화: 반복 append vs Array(repeating:count:) (0) | 2025.11.11 |


