
SwiftPM 의존성을 붙였다고 Swift API 를 그대로 쓸 수 있는 게 아니더라고요. struct 도 async 도 코틀린 쪽에선 깡그리 사라집니다.
KMP iOS 모듈 하나 굴리고 있는데, 코틀린(Kotlin) 2.4.0(이달 초에 stable 로 풀린) 에 들어온 swiftPMDependencies DSL 을 지난 주말에 사이드 브랜치로 한 번 붙여봤어요. 코코아팟(CocoaPods) 졸업 시기가 코앞이라 더 이상 미룰 대목도 아니었고요. 막상 붙여보니 "Swift 패키지를 의존성으로 바로 먹는다"는 헤드라인이 실제로는 절반쯤만 맞는 얘기였습니다.
KMP iOS SwiftPM 의존성, swiftPMDependencies DSL 첫인상
build.gradle.kts 안에서 cocoapods 블록을 쓰던 자리를 거의 1:1 로 옮길 수 있게 만들어 놓은 모양새예요.
kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
swiftPMDependencies {
remotePackage(
"https://github.com/firebase/firebase-ios-sdk.git",
from = "12.6.0"
) {
products("FirebaseAnalytics")
}
}
}
Package.swift 파일을 따로 만질 일이 없다는 점이 제일 마음에 들었습니다. 버전 카탈로그(libs.versions.toml) 에서 안드로이드(Android)·iOS 의존성 버전을 한자리에서 관리하니까 "안드로이드 SDK 19 쓰는데 iOS SDK 17" 같은 미스매치가 빌드 단계에서 잡히기 시작하더라고요.
빌드 시간은 늘긴 늘었어요. 평소 65초 안팎으로 끝나던 첫 sync 가 110초까지 늘어진 구간이 있었습니다. 110초가 어느 정도냐면, 커피 한 잔 내리고 자리에 와도 아직 sync 가 돌고 있는 정도예요. 다행히 캐시가 깔린 두 번째 sync 부터는 40초대로 내려와서 일상 개발 흐름엔 큰 부담이 없었습니다.

Swift struct·async, 코틀린 쪽에선 안 보입니다
여기서 진짜 함정이 나왔어요. SwiftPM 의존성을 추가했다고 패키지 안의 Swift API 가 다 보이는 게 아니거든요.
동작 원리가 Clang 모듈 import 기반이라 그렇습니다. 쉽게 말하면 그 Swift 패키지가 Objective-C 헤더로 노출해 둔 API 만 Kotlin/Native 쪽에서 보인다는 얘기예요. @objc 어노테이션이 붙었거나 NSObject 를 상속한 클래스만 자동 발견 대상이 되는 것으로 알려져 있어요.
그래서 다음 셋은 코틀린 쪽에서 통째로 사라집니다.
- Swift
struct로 정의된 모델 타입 - 제네릭 사용한 API
async/await시그니처 함수
파이어베이스(Firebase) 의 일부 신규 API 가 struct 기반으로 옮겨가는 중이라 막상 import FirebaseAnalytics 같은 모듈을 잡아도 원했던 함수가 nil 로 안 보이는 경우가 생겼어요. 그 대목은 결국 Objective-C 래퍼 클래스를 한 단 더 끼워서 우회했습니다. "SPM 의존성을 붙였다"는 게 "Swift API 전체를 그대로 쓴다"는 뜻이 아니라는 점, 처음 도입할 때 머리에 박아두셔야 해요.
참고로 KMP 코드를 Swift 친화적으로 export 해주는 SKIE 와는 역할이 정반대입니다. SKIE 는 내보내기(export), SwiftPM import 는 들여오기라서 둘은 같이 써도 충돌하지 않습니다.

CocoaPods 졸업 시기, 점진적 마이그레이션 동선
코코아팟이 2026년 12월부터 저장소를 read-only 로 전환한다고 합니다. 파이어베이스 iOS SDK 는 그보다 두 달 빠른 2026년 10월부터 코코아팟 신규 버전 게시를 멈춘다는 발표가 있었어요. KMP 진영 입장에서 SwiftPM 전환은 이제 선택이 아니라 일정 잡힌 숙제가 된 셈입니다.
다행히 기존 cocoapods 블록과 swiftPMDependencies 블록은 공존이 가능합니다. 저는 이번에 의존성 단위로 끊어서 옮기는 동선을 잡았어요.
- 외부 의존성이 적고 Objective-C 인터페이스가 명확한 라이브러리부터 SPM 으로 이관
- 파이어베이스처럼 라이브러리가 큰 자리는 모듈 분리 후 한 모듈씩 전환
- 코코아팟 전용 의존성은 마지막까지 남겨두고, Swift 래퍼만 별도 관리
한 번에 다 갈아엎으려다 CI 가 꺼지는 사고가 더 무섭더라고요. 점진적 동선이 결국 제일 빠른 길이었습니다.

Kotlin/Native 2.4 CMS GC 전환 체감
이번 2.4 에서 같이 들어온 CMS GC 기본 적용도 결과적으로는 SPM 전환 체감을 거들었습니다. 기존 STW(Stop-The-World) 구간에서 잡혔던 60fps 드랍이 부드러워지는 포인트가 보였어요. 바이너리 크기나 앱 시작 시간 자체는 코코아팟 시절과 큰 차이가 없는 것으로 보입니다.
swiftPMDependencies Experimental 한계와 spm4Kmp 대안
좋은 얘기만 하긴 어려운 대목도 있습니다. 지금 시점에서 swiftPMDependencies DSL 은 Experimental 단계라는 얘기이고, 다음 RC 에서 시그니처가 바뀔 여지가 남아 있어요. 라이브러리 모듈을 다시 Swift Package 로 export 하는 경로는 아직 막혀 있어서, 라이브러리 배포 파이프라인 쓰시는 분들은 XCFramework 흐름을 유지하셔야 합니다. macOS + Xcode 환경에서만 resolve 가 도는 점도 그대로고요.
개인적으로는 공식 기능이 GA 되기 전까지는 커뮤니티 플러그인인 spm4Kmp 를 비교 후보로 같이 올려두시는 걸 추천드리고 싶네요. 공식 기능보다 먼저 나와서 예외 케이스를 더 많이 처리해놓은 구간이 있습니다. 다만 두 플러그인을 한 모듈에 같이 끼우면 충돌이 나기 쉬워서, 모듈 단위로 한 쪽만 쓰는 게 깔끔해요.
다음 글 떡밥
이번엔 의존성 들여오는 import 쪽만 다뤘는데, 반대 방향인 KMP → Swift export 쪽은 SKIE 가 2026년 들어 async/await 매핑을 어디까지 다루는지가 또 다른 이야기예요. 그 대목은 별도 글에서 실측 코드와 같이 정리해볼 생각입니다.
'Android 개발 > Kotlin' 카테고리의 다른 글
| 직렬화 라이브러리 3개를 kotlinx.serialization 한 곳으로 정리한 후기 (0) | 2026.07.14 |
|---|---|
| 코틀린 sealed Result 패턴으로 try/catch 좀비를 잡은 3주 (0) | 2026.07.07 |
| Kotlin 2.4 field 키워드로 ViewModel _state/state 보일러플레이트 한 줄로 줄이기 (0) | 2026.06.27 |
| 공유 UI 말고 공유 코어부터, KMP 도입 한 주 일지 (0) | 2026.06.24 |
| Kotlin 2.4 UUID 안정화로 안드 데이터 클래스에서 라이브러리 두 개 지운 후기 (0) | 2026.06.21 |