SPM(Swift Package Manager)은 Apple이 서드파티 라이브러리의 연결, 빌드 및 업데이트를 자동화하기 위해 개발한 Swift 생태계의 내장 패키지 관리자입니다. SPM은 버전 3.0(2016년)부터 Swift 컴파일러의 일부였으며 별도 설치가 필요하지 않습니다. CocoaPods 및 Carthage와 달리 SPM은 컴파일러 및 Xcode와 직접 통합되어 최신 Swift 프로젝트에서 표준 의존성 관리 도구가 되었습니다. 이 기사에서는 Package.swift의 구조, SPM 명령어, 자체 패키지 생성 및 대체 관리자에서 마이그레이션에 대해 설명합니다.
핵심 요점
SPM(Swift Package Manager)는 Swift 언어를 위한 공식 패키지 관리자로, swiftc 컴파일러 및 Xcode 개발 환경에 내장되어 있습니다. 개발자는 이를 사용하여 서드파티 라이브러리를 추가하고, 버전을 관리하며, 자체 패키지를 게시할 수 있습니다. SPM은 Swift 3.0(2016년 9월)에서 명령줄 도구로 처음 등장했으며, Xcode 11(2019년)부터 그래픽 인터페이스와 완전히 통합되어 File → Add Packages 메뉴를 통해 의존성을 추가할 수 있습니다.
SPM은 Git 리포지토리에서 의존성의 소스 코드를 자동으로 다운로드하고, 메인 프로젝트와 병렬로 빌드하며, 결과를 캐시하여 후속 빌드를 더 빠르게 실행합니다. CocoaPods와 달리 SPM은 별도의 작업 공간(xcworkspace)을 생성하지 않으며 의존성이 메인 Xcode 프로젝트의 일부가 됩니다. Swift.org Developer Survey(2024)에 따르면 iOS 개발자의 67%가 SPM을 사용하여 Swift 생태계에서 가장 인기 있는 의존성 관리 도구가 되었습니다.
SPM은 세 가지 플랫폼을 지원합니다: Apple(iOS, macOS, tvOS, watchOS, visionOS), Linux(Ubuntu, CentOS, Amazon Linux) 및 서버 측 Swift(Vapor, Kitura). Linux에서 SPM은 Xcode 없이 완전히 명령줄을 통해 작동합니다.
SPM은 세 가지 핵심 개념을 중심으로 구축되었습니다: 패키지(packages), 제품(products) 및 대상(targets). 패키지는 Package.swift 매니페스트가 있는 Git 리포지토리입니다. 제품은 빌드 결과(라이브러리 또는 실행 파일)입니다. 대상은 패키지 내의 모듈로, 빌드 단위로 컴파일됩니다.
개발자가 Package.swift에 의존성을 추가하면 SPM은 다음 단계를 수행합니다:
~Library/Caches/org.swift.swiftpm/에 저장합니다.Package.resolved 파일은 모든 의존성의 정확한 버전을 고정하여 개발 팀이 동일한 라이브러리 세트로 작업하도록 합니다. 이 파일은 버전 관리(git)에 추가해야 합니다.
대안에 비해 SPM의 주요 장점은 중앙 집중식 레지스트리가 필요 없다는 것입니다. 패키지는 GitHub, GitLab, Bitbucket 등 모든 공개 Git 리포지토리뿐만 아니라 회사 사설 Git 서버에도 있을 수 있습니다. Swift 5.2부터 SPM은 소스 코드를 제공하지 않고 XCFramework로 배포되는 바이너리 의존성(바이너리 대상)을 지원합니다.
Package.swift는 패키지 구조와 의존성을 설명하는 Swift 파일입니다. 파일은 Swift 자체로 작성되며(JSON 또는 YAML 아님), 매니페스트 내에서 조건부 로직, 계산된 상수 및 함수를 사용할 수 있습니다.
Package.swift 기본 구조:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyLibrary",
platforms: [
.iOS(.v16),
.macOS(.v13)
],
products: [
.library(
name: "MyLibrary",
targets: ["MyLibrary"]
),
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git",
from: "5.9.0"),
.package(url: "https://github.com/onevcat/Kingfisher.git",
from: "7.12.0"),
],
targets: [
.target(
name: "MyLibrary",
dependencies: [
"Alamofire",
"Kingfisher"
]
),
.testTarget(
name: "MyLibraryTests",
dependencies: ["MyLibrary"]
),
]
)
주요 요소를 살펴보겠습니다:
// swift-tools-version: 5.9 — SPM 버전을 지정하는 지시문; 사용 가능한 매니페스트 구문이 이에 따라 달라집니다.name — Xcode에 표시되고 의존성 링크에 사용되는 패키지 이름입니다.platforms — 최소 플랫폼 버전; SPM은 이전 OS 버전에서 패키지 빌드를 허용하지 않습니다.products — 패키지가 "내보내는" 것: 라이브러리(.library) 또는 실행 파일(.executable).dependencies — URL 및 버전이 있는 외부 패키지 목록; from:, exact:, branch:, revision:을 지원합니다.targets — 빌드 대상; 각 대상에는 해당 디렉토리(Sources/TargetName/)의 의존성, 리소스 및 swift 파일 목록이 포함됩니다.정확한 버전, 브랜치 및 커밋 지정 예:
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git",
exact: "1.17.3"),
.package(url: "https://github.com/pointfreeco/swift-composable-architecture.git",
branch: "main"),
.package(url: "https://github.com/apple/swift-log.git",
revision: "e5c6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4"),
]
Swift 5.9부터 Package.swift에 static framework 및 linkerSettings 지원이 추가되어 정적 및 동적 라이브러리에 대한 더 정밀한 링커 구성이 가능해졌습니다.
Swift Package Manager는 터미널을 통해 작업하기 위한 명령어 세트를 제공합니다. 명령어는 패키지 루트 디렉토리(Package.swift가 있는 위치)에서 실행됩니다.
# 라이브러리로 새 패키지 생성
swift package init --type library
# 실행 가능 패키지 생성(콘솔 애플리케이션)
swift package init --type executable
# 프로젝트 빌드
swift build
# 릴리스 구성으로 빌드
swift build -c release
# 테스트 실행
swift test
# 특정 테스트 실행
swift test --filter "MyLibraryTests/testExample"
# 의존성 다운로드 및 해결
swift package resolve
# 의존성을 최신 버전으로 업데이트
swift package update
# 의존성 그래프 표시
swift package show-dependencies
# 빌드 캐시 정리
swift package clean
# Xcode 프로젝트 생성(Xcode 11 이전)
swift package generate-xcodeproj
Xcode 내에서 작업할 때 이러한 명령어의 대부분은 자동으로 실행됩니다: 프로젝트를 열면 의존성이 해결되고, ⌘B로 빌드, ⌘U로 테스트가 시작됩니다. 그러나 Xcode를 사용할 수 없는 CI/CD 파이프라인(GitHub Actions, GitLab CI, Jenkins)에서는 터미널 명령어를 알아야 합니다.
swift package resolve 명령어는 Package.resolved 파일을 생성하거나 업데이트합니다. 이 파일은 전이적 의존성을 포함한 모든 의존성의 정확한 버전을 고정하며 git에 추가해야 합니다. 최신 라이브러리 버전으로 작업하려면 새 기능 브랜치 전에 swift package update를 실행하는 것이 좋습니다.
자체 SPM 패키지를 만들면 다중 모듈 프로젝트에서 비즈니스 로직을 캡슐화하고 오픈 소스 라이브러리를 게시하는 데 유용합니다. 단계별 프로세스를 살펴보겠습니다.
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library
swift package init 명령어는 다음 구조를 생성합니다:
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│ └── MyNetworkKit/
│ └── MyNetworkKit.swift
└── Tests/
└── MyNetworkKitTests/
└── MyNetworkKitTests.swift
SPM은 자동으로 Sources/ 및 Tests/ 디렉토리를 스캔합니다. Sources 내의 각 하위 디렉토리는 대상(target)에 해당합니다.
의존성을 추가하고 대상 플랫폼을 구성해 보겠습니다:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyNetworkKit",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(
name: "MyNetworkKit",
targets: ["MyNetworkKit"]
),
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git",
from: "5.9.0"),
],
targets: [
.target(
name: "MyNetworkKit",
dependencies: ["Alamofire"]
),
.testTarget(
name: "MyNetworkKitTests",
dependencies: ["MyNetworkKit"]
),
]
)
// Sources/MyNetworkKit/MyNetworkKit.swift
import Foundation
import Alamofire
public struct NetworkClient {
private let session: Session
public init() {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
self.session = Session(configuration: configuration)
}
public func fetchData(from url: String) async throws -> Data {
let response = try await session.request(url).serializingData().value
return response
}
}
패키지를 Git 리포지토리에 푸시하고 SemVer 태그를 만듭니다:
git init
git add .
git commit -m "Initial commit: MyNetworkKit"
git remote add origin https://github.com/username/MyNetworkKit.git
git push -u origin main
git tag 1.0.0
git push --tags
이후 모든 개발자는 .package(url: "https://github.com/username/MyNetworkKit.git", from: "1.0.0")을 사용하여 패키지를 추가할 수 있습니다.
Alamofire는 Swift에서 가장 인기 있는 HTTP 클라이언트입니다. SPM을 통해 추가하고 GET 요청을 해보겠습니다.
import Alamofire
func fetchUsers() {
AF.request("https://jsonplaceholder.typicode.com/users")
.validate()
.responseDecodable(of: [User].self) { response in
switch response.result {
case .success(let users):
print("(users.count)명의 사용자 수신")
case .failure(let error):
print("오류: (error.localizedDescription)")
}
}
}
Swinject 라이브러리는 Swift용 DI 컨테이너를 제공합니다. .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0")을 통해 추가됩니다.
import Swinject
let container = Container()
container.register(NetworkServiceProtocol.self) { _ in NetworkService() }
container.register(DataRepositoryProtocol.self) { r in
DataRepository(networkService: r.resolve(NetworkServiceProtocol.self)!)
}
let repository = container.resolve(DataRepositoryProtocol.self)
repository?.loadData()
Apple의 swift-log 패키지는 여러 백엔드(OSLog, 콘솔, 파일)를 지원하는 통합 로깅 API를 제공합니다.
import Logging
var logger = Logger(label: "com.myapp.network")
logger.logLevel = .debug
logger.info("네트워크 요청 시작됨", metadata: [
"url": "(requestURL)",
"method": "GET"
])
logger.warning("응답 시간이 2초 초과")
logger.error("연결 오류: 인터넷 없음")
이 세 가지 예제는 SPM의 일반적인 사용 시나리오(HTTP 클라이언트, DI 컨테이너 및 시스템 인프라)를 다룹니다. 라이브러리 선택은 우연이 아닙니다 — Alamofire, Swinject 및 swift-log는 GitHub에서 가장 많은 별표를 받은 Swift 패키지 상위 20위 안에 있습니다.
프로젝트가 CocoaPods 또는 Carthage를 사용하는 경우 SPM으로의 마이그레이션은 몇 단계로 수행됩니다. 이 프로세스는 안전합니다. SPM 의존성은 동일한 프로젝트에서 CocoaPods 및 Carthage와 공존할 수 있어 점진적인 마이그레이션이 가능합니다.
.xcworkspace를 제거하고 .xcodeproj를 연 후 Clean Build Folder를 실행합니다.rm -rf Carthage/ 실행.2025년 기준으로 SPM은 대부분의 인기 Swift 라이브러리를 지원합니다. 예외는 모듈 맵이 없는 일부 ObjC 프레임워크입니다. 라이브러리가 아직 SPM을 지원하지 않는 경우 README의 Installation 섹션을 확인하세요. 대부분의 작성자는 최신 버전에서 이미 SPM 지원을 추가했습니다.
자주 묻는 질문
SPM은 Swift 컴파일러 및 Xcode에 내장되어 있어 gem이나 Homebrew를 통한 설치가 필요하지 않습니다. CocoaPods는 중앙 집중식 Specs 레지스트리를 사용하고 별도의 작업 공간을 생성합니다. Carthage는 프로젝트 통합 없이 프레임워크를 통해 작동합니다. SPM은 컴파일러 수준에서 통합된 유일한 관리자로, 의존성이 메인 코드와 병렬로 해결, 캐시 및 빌드됩니다.
예, SPM은 Swift + Objective-C 혼합 프로젝트를 지원합니다. SPM 패키지 내의 ObjC 파일은 올바른 modulemap이 있는 경우 자동으로 Umbrella Header에 포함됩니다. 그러나 SPM은 모듈 맵이 없는 정적 ObjC 라이브러리를 지원하지 않습니다. ObjC 라이브러리는 modulemap을 제공하거나 순수 C로 작성된 경우에만 SPM을 통해 연결하는 것이 좋습니다.
SPM은 시맨틱 버전 관리(SemVer)를 사용합니다. 패키지 A에 Alamofire 5.8+가 필요하고 패키지 B에 Alamofire 5.9+가 필요한 경우 SPM은 둘 다 충족하는 버전 5.9.x를 선택합니다. 충돌을 해결할 수 없는 경우(한 패키지는 5.x, 다른 패키지는 6.x 필요) SPM은 오류를 보고합니다. 이 경우 패키지 중 하나를 업데이트하거나 의존성을 두 요구 사항과 모두 호환되는 버전으로 변경해야 합니다.
macOS: ~Library/Caches/org.swift.swiftpm/ 및 ~/Library/Developer/Xcode/DerivedData/. Linux: ~cache/swiftpm/. 빌드 중 SPM은 소스 코드와 컴파일된 오브젝트 파일을 캐시합니다. 캐시를 완전히 지우려면 swift package reset을 실행하세요 — 이 명령어는 현재 프로젝트의 의존성 캐시와 DerivedData를 제거합니다.
예, Swift 5.2부터 SPM은 바이너리 대상을 지원합니다. 클로즈드 소스 라이브러리는 XCFramework로 배포되며 .xcframework 경로가 Package.swift에 지정됩니다. 소스 코드는 공개되지 않습니다. 바이너리 대상은 .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework")를 통해 지정됩니다. 이를 통해 라이선스 계약을 위반하지 않고 상용 SDK를 연결할 수 있습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.