Swift Package Manager 2026 완벽 가이드: 패키지 생성, 배포, 면접 질문
Swift Package Manager 사용법을 상세히 설명합니다. 패키지 생성, 의존성 관리, 라이브러리 배포 방법, iOS 기술 면접에서 자주 나오는 질문과 답변을 다루는 종합 튜토리얼입니다.

Swift Package Manager(SPM)는 Swift 프로젝트의 의존성 관리와 패키지 배포를 담당합니다. CocoaPods나 Carthage와 달리 SPM은 Xcode와 Swift 툴체인에 직접 통합되어 있어 외부 도구 설치가 필요 없습니다. Swift 6.2와 6.3에서는 엄격한 메모리 안전성 설정, 기본 액터 격리, 새로운 Swift Build 시스템이 도입되어 SPM이 현대적인 iOS 개발의 핵심 선택지가 되었습니다.
Swift Package Manager는 의존성 해결, 소스 코드 다운로드, 모듈 컴파일, 최종 바이너리 링킹을 수행합니다. 단일 Package.swift 매니페스트 파일에서 의존성, 타겟, 제품, 빌드 설정을 모두 정의할 수 있습니다.
Package.swift 매니페스트 구조와 문법
모든 Swift 패키지는 저장소 루트에 있는 Package.swift 파일에서 시작합니다. 이 매니페스트는 Swift 코드를 사용하여 패키지 구조, 의존성, 빌드 구성을 선언합니다.
import PackageDescription
let package = Package(
name: "NetworkKit",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(
name: "NetworkKit",
targets: ["NetworkKit"]
)
],
dependencies: [
.package(
url: "https://github.com/Alamofire/Alamofire.git",
from: "5.9.0"
)
],
targets: [
.target(
name: "NetworkKit",
dependencies: ["Alamofire"]
),
.testTarget(
name: "NetworkKitTests",
dependencies: ["NetworkKit"]
)
]
)platforms 배열은 최소 배포 타겟을 지정합니다. products 섹션은 다른 패키지에서 임포트할 수 있는 항목을 정의합니다. targets 섹션은 각각의 의존성을 가진 컴파일 단위를 나열합니다.
명령줄에서 Swift 패키지 생성
swift package init 명령은 표준 디렉토리 구조를 가진 새 패키지의 스캐폴드를 생성합니다. --type 플래그는 패키지가 라이브러리를 생성할지 실행 파일을 생성할지 결정합니다.
# Create a library package
mkdir NetworkKit && cd NetworkKit
swift package init --type=library
# Generated structure:
# NetworkKit/
# ├── Package.swift
# ├── Sources/
# │ └── NetworkKit/
# │ └── NetworkKit.swift
# └── Tests/
# └── NetworkKitTests/
# └── NetworkKitTests.swiftswift build를 실행하면 패키지가 컴파일됩니다. swift test를 실행하면 테스트 스위트가 실행됩니다. 두 명령 모두 추가 설정 없이 Package.swift의 구성을 사용합니다.
의존성 버전 요구사항과 해결
SPM은 세 가지 버전 지정 전략을 지원합니다: 정확한 버전, 버전 범위, 브랜치 또는 커밋 참조입니다. 선택에 따라 재현성과 유연성이 달라집니다.
dependencies: [
// Semantic versioning: 5.9.0 up to next major
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.9.0"),
// Exact version: locks to 5.9.1
.package(url: "https://github.com/Alamofire/Alamofire.git", exact: "5.9.1"),
// Version range: 5.8.0 to 5.9.9
.package(url: "https://github.com/Alamofire/Alamofire.git", "5.8.0".."5.9.9"),
// Branch reference: for development
.package(url: "https://github.com/Alamofire/Alamofire.git", branch: "main"),
// Commit reference: pinned to specific commit
.package(url: "https://github.com/Alamofire/Alamofire.git", revision: "abc123")
]Package.resolved 파일은 의존성 해결 시 결정된 정확한 버전을 기록합니다. 이 파일은 팀원과 CI 시스템 전반에서 재현 가능한 빌드를 보장하기 위해 버전 관리에 커밋해야 합니다.
브랜치 및 커밋 참조는 시맨틱 버저닝을 우회합니다. 프로덕션 앱에서 branch: "main"을 사용하면 main에 대한 푸시로 빌드가 깨질 수 있습니다. 브랜치 참조는 미출시 기능에 대한 활발한 개발에만 사용하는 것이 좋습니다.
Xcode 프로젝트에 패키지 추가
Xcode는 파일 메뉴를 통해 SPM을 통합합니다. File에서 Add Package Dependencies로 이동합니다. 저장소 URL을 입력하고 버전 규칙을 선택한 후 어떤 타겟이 의존성을 링크해야 하는지 선택합니다.
import UIKit
import Alamofire // Available after adding via Xcode
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
AF.request("https://api.example.com/health").response { response in
print(response.result)
}
return true
}
}Xcode는 패키지 참조를 .xcodeproj 파일에 저장하고 해결된 버전을 프로젝트 루트의 Package.resolved에 저장합니다. Derived Data 폴더는 다운로드된 패키지를 캐시합니다.
경로 의존성을 사용한 로컬 패키지 개발
개발 중에는 원격 URL 대신 로컬 경로를 지정하여 중간 버전을 배포하지 않고도 빠른 반복이 가능합니다. 이 기법은 모노레포 설정과 기능 개발에 적합합니다.
dependencies: [
// Local path for development
.package(path: "../NetworkKit"),
// Remote URL for release
// .package(url: "https://github.com/company/NetworkKit.git", from: "1.0.0")
]비활성 줄을 주석 처리하여 로컬과 원격 의존성 간에 전환합니다. Xcode와 Swift CLI는 소비 패키지를 기준으로 상대 경로의 의존성을 해결합니다.
iOS 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
GitHub에 Swift 패키지 배포
배포에는 시맨틱 버전 태그가 있는 Git 저장소가 필요합니다. SPM은 Git 태그를 릴리스 버전으로 처리합니다. Swift Package Index는 공개 패키지를 자동으로 인덱싱합니다.
# Initialize repository and push
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/username/NetworkKit.git
git push -u origin main
# Create version tag
git tag 1.0.0
git push origin 1.0.0
# Other packages can now depend on:
# .package(url: "https://github.com/username/NetworkKit.git", from: "1.0.0")시맨틱 버저닝 규칙을 따릅니다: 호환되지 않는 변경에는 메이저 버전, 새 기능에는 마이너 버전, 버그 수정에는 패치 버전을 증가시킵니다. SPM은 from: 버전 요구사항을 해결할 때 이 규칙 준수를 가정합니다.
Swift 6.2 및 6.3 패키지 설정
Swift 6.2에서는 타겟별 언어 모드 구성과 엄격한 메모리 안전성 설정이 도입되었습니다. Swift 6.3에서는 Swift Build 통합이 옵트인 기능으로 추가되었습니다.
import PackageDescription
let package = Package(
name: "SafeNetworkKit",
platforms: [.iOS(.v17)],
products: [
.library(name: "SafeNetworkKit", targets: ["SafeNetworkKit"])
],
targets: [
.target(
name: "SafeNetworkKit",
swiftSettings: [
// Enable Swift 6 language mode for this target only
.swiftLanguageMode(.v6),
// Enable strict memory safety checking (SE-0458)
.enableExperimentalFeature("StrictMemorySafety"),
// Set default actor isolation (SE-0466)
.enableExperimentalFeature("GlobalActorIsolation")
]
)
]
)swiftLanguageMode 설정을 통해 코드베이스 전체에서 Swift 6 기능을 점진적으로 채택할 수 있습니다. 동일한 패키지 내의 타겟들이 서로 다른 언어 모드를 사용할 수 있습니다.
보안 컴플라이언스를 위한 SBOM 생성
SE-0509에 의해 Swift 6.3에서 네이티브 Software Bill of Materials 생성이 추가되었습니다. SBOM 문서는 보안 감사와 규제 준수를 위해 모든 의존성과 버전을 나열합니다.
# Generate SBOM in CycloneDX format
swift build --sbom-spec cyclonedx
# Generate SBOM using dedicated subcommand
swift package generate-sbom --format spdx
# Output includes:
# - Package name and version
# - All transitive dependencies
# - License information
# - Source repository URLs엔터프라이즈 환경과 정부 계약에서 SBOM 문서화 요구사항이 증가하고 있습니다. CycloneDX와 SPDX 형식은 표준 취약점 스캔 도구와 통합됩니다.
바이너리 타겟과 XCFramework 배포
바이너리 타겟을 사용하면 소스 코드 대신 사전 컴파일된 프레임워크를 배포할 수 있습니다. XCFramework는 여러 플랫폼과 아키텍처용 바이너리를 번들로 묶습니다.
import PackageDescription
let package = Package(
name: "AnalyticsSDK",
platforms: [.iOS(.v14)],
products: [
.library(name: "AnalyticsSDK", targets: ["AnalyticsSDK"])
],
targets: [
.binaryTarget(
name: "AnalyticsSDK",
url: "https://releases.example.com/AnalyticsSDK-2.0.0.xcframework.zip",
checksum: "abc123def456..."
)
]
)
// Generate checksum:
// swift package compute-checksum AnalyticsSDK-2.0.0.xcframework.zip바이너리 타겟은 소비자의 빌드 시간을 단축하고 독점 구현을 보호합니다. 체크섬은 다운로드 무결성을 보장합니다.
Swift Package Manager 관련 일반적인 면접 질문
iOS 포지션 기술 면접에서는 SPM 관련 질문이 자주 포함됩니다. 기본 사용법부터 아키텍처 결정까지 다양합니다.
Q: SPM은 CocoaPods 및 Carthage와 어떻게 다릅니까?
SPM은 외부 도구 없이 Xcode와 Swift 툴체인에 통합됩니다. CocoaPods는 중앙 사양 저장소를 사용하고 Xcode 워크스페이스 구조를 수정합니다. Carthage는 Xcode 통합 없이 별도의 단계에서 프레임워크를 빌드합니다. SPM은 단일 통합 프로세스에서 의존성을 해결하고 빌드합니다. Apple의 swift 패키지 문서에서 공식 도구를 확인할 수 있습니다.
Q: 의존성 해결 중에 무슨 일이 일어납니까?
SPM은 재귀적으로 Package.swift 매니페스트를 읽어 의존성 그래프를 구축합니다. 그런 다음 버전 제약을 적용하여 모든 패키지에 호환되는 버전을 찾습니다. 리졸버는 정확한 버전을 Package.resolved에 기록합니다. 두 패키지가 공유 의존성의 호환되지 않는 버전을 요구하면 충돌이 발생합니다.
Q: 다이아몬드 의존성 충돌을 어떻게 처리합니까?
패키지 A와 B가 모두 호환되지 않는 버전 요구사항으로 패키지 C에 의존하면 SPM은 해결에 실패합니다. 해결책에는: 한 소비자를 더 넓은 버전 범위를 지원하도록 업데이트, 제한적인 패키지 포크, 또는 동일한 모듈 이름을 가진 서로 다른 패키지 간 충돌의 경우 모듈 별칭 사용이 포함됩니다.
// Module aliasing for name conflicts
.target(
name: "MyApp",
dependencies: [
.product(name: "Logging", package: "swift-log", moduleAliases: ["Logging": "SwiftLogging"])
]
)Q: 소스 배포 대신 바이너리 타겟을 언제 사용합니까?
바이너리 타겟은 소스 노출이 허용되지 않는 독점 SDK, 컴파일 시간이 개발자 생산성에 영향을 미치는 대규모 의존성, 사전 빌드된 벤더 라이브러리에 적합합니다. 의존성 디버깅이 가치를 더하는 오픈 소스 프로젝트와 내부 라이브러리에는 소스 배포가 여전히 선호됩니다.
CocoaPods에서 Swift Package Manager로 마이그레이션
마이그레이션에는 Podfile 항목을 SPM 패키지 참조로 교체하는 작업이 필요합니다. 모든 CocoaPods에 SPM 동등물이 있는 것은 아니므로 시작 전에 Swift Package Index에서 가용성을 확인하는 것이 좋습니다.
# Before: Podfile
pod 'Alamofire', '~> 5.9'
pod 'SwiftyJSON', '~> 5.0'
pod 'Kingfisher', '~> 7.0'// After: Package dependencies in Xcode
// File > Add Package Dependencies for each:
// https://github.com/Alamofire/Alamofire.git from 5.9.0
// https://github.com/SwiftyJSON/SwiftyJSON.git from 5.0.0
// https://github.com/onevcat/Kingfisher.git from 7.0.0마이그레이션 후 Podfile, Podfile.lock, Pods 디렉토리를 삭제합니다. pod deintegrate를 실행하여 CocoaPods 워크스페이스 수정을 제거합니다. Swift 파일의 import 문은 변경되지 않습니다.
프로덕션 앱을 위한 SPM 모범 사례
- 안정성을 위해
from:으로 메이저 버전을 고정하면서 마이너 업데이트와 패치를 받음 - 팀 전체에서 재현 가능한 빌드를 보장하기 위해
Package.resolved를 커밋 - 활발한 개발 중에는 로컬 패키지를 사용하고 머지 전에 원격 URL로 전환
- 릴리스 빌드에 번들되지 않도록
testTarget을 사용하여 테스트 전용 의존성 분리 - 매니페스트 상단에 최소 Swift 도구 버전 문서화:
// swift-tools-version: 5.10 - 릴리스 태깅 전에 CI에서 패키지 빌드 검증
// swift-tools-version: 주석은 Package.swift의 첫 번째 줄에 나타나야 합니다. 이것은 매니페스트가 사용하는 PackageDescription API 버전을 결정합니다. Swift 6.0에서는 라이선스 헤더를 위해 이 주석을 후속 줄에 배치할 수 있는 유연성이 추가되었습니다.
iOS 개발자를 위한 SPM 핵심 사항
- SPM은 Xcode에 네이티브로 통합되어 CocoaPods와 Carthage 설정 오버헤드를 제거함
Package.swift매니페스트는 Swift 코드를 사용하여 타입 체크된 의존성 선언을 가능하게 함- 버전 요구사항은 시맨틱 버저닝 범위, 정확한 버전, 브랜치 참조를 지원함
Package.resolved는 재현 가능한 빌드를 위해 의존성 버전을 잠금- Swift 6.2에서 타겟별 언어 모드와 엄격한 메모리 안전성 설정이 추가됨
- Swift 6.3에서 컴플라이언스 문서화를 위한 SBOM 생성이 도입됨
- 바이너리 타겟은 소스 배포가 실용적이지 않을 때 사전 컴파일된 XCFramework를 배포함
- 면접 질문은 해결 충돌, 마이그레이션 전략, SPM과 대안 간의 아키텍처 트레이드오프에 초점을 맞춤
종합적인 iOS 면접 준비를 위해 SharpSkill의 SwiftUI 상태 관리와 프로토콜 지향 프로그래밍 모듈을 확인하시기 바랍니다.
연습을 시작하세요!
면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.
iOS 코드의 버그를 찾을 수 있나요
실제 코드 한 조각, 숨은 버그 하나, 하루 한 번. 계정 없이 바로 도전할 수 있습니다.

작성자
Anthony Fillion-MailletSharpSkill 창업자
10년 이상 풀스택 개발을 해왔습니다. SharpSkill을 운영하며 이곳에 게시되는 모든 내용에 책임을 집니다.
2026년 9월 20일 업데이트
공유
관련 기사

2026년 SwiftUI와 CloudKit: 디바이스 간 데이터 동기화 패턴
SwiftUI로 CloudKit 동기화를 구현하기 위한 완전한 가이드: CKSyncEngine, SwiftData 통합, 충돌 해결 및 iOS 2026 모범 사례.

Swift에서 Combine vs async/await: 점진적 마이그레이션 패턴
Swift에서 Combine에서 async/await로 마이그레이션하는 완전한 가이드: 점진적 전략, 브리징 패턴, iOS 코드베이스의 패러다임 공존.

2026년 iOS 접근성 면접 질문: VoiceOver와 Dynamic Type
iOS 면접 대비를 위한 핵심 접근성 질문: VoiceOver, Dynamic Type, 시맨틱 traits, 접근성 감사.