앱 하나 만들려면 iOS 따로, Android 따로 개발해야 하는 시대는 서서히 저물고 있습니다. 특히 Kotlin Multiplatform(KMP)은 네이티브 성능을 포기하지 않으면서도 비즈니스 로직을 공유할 수 있다는 점에서 최근 모바일 개발자들 사이에서 뜨거운 관심을 받고 있죠. 저도 실제 프로젝트에 KMP를 도입해보면서 느낀 점들이 많은데요, 오늘은 그 경험을 바탕으로 솔직하게 이야기해보겠습니다.
1. Kotlin Multiplatform이란 무엇인가
1-1. KMP의 핵심 개념
Kotlin Multiplatform은 JetBrains에서 만든 크로스플랫폼 기술로, 하나의 Kotlin 코드베이스에서 Android, iOS, 웹, 데스크톱 등 여러 플랫폼을 동시에 타깃할 수 있게 해줍니다. 여기서 중요한 포인트는 "UI까지 공유하자"가 아니라 "비즈니스 로직을 공유하자"라는 철학입니다. UI는 각 플랫폼 네이티브로 구현하고, 데이터 처리·네트워크 통신·상태 관리 같은 핵심 로직만 공통 모듈에 작성하는 방식이죠.
Flutter나 React Native가 UI까지 통째로 공유하는 접근이라면, KMP는 각 플랫폼의 네이티브 UI 경험을 100% 살리면서 중복 코드만 줄이는 전략입니다. 그래서 기존 네이티브 앱에 점진적으로 도입하기가 훨씬 수월합니다.
1-2. KMP vs Compose Multiplatform 차이
많이들 헷갈리시는 부분인데, KMP와 Compose Multiplatform은 다른 개념입니다. KMP는 로직 공유를 위한 프레임워크이고, Compose Multiplatform은 그 위에 UI까지 공유할 수 있도록 확장한 것입니다. Compose Multiplatform을 사용하면 Jetpack Compose 스타일의 선언형 UI를 iOS에서도 쓸 수 있게 되는 거죠. 다만 Compose Multiplatform의 iOS 지원은 아직 성숙도 측면에서 프로덕션에 바로 적용하기엔 고민이 필요한 단계입니다. 저는 개인적으로 로직만 공유하는 순수 KMP 방식을 먼저 추천드립니다.
1-3. 실제 도입 사례들
Netflix, Cash App, Philips, McDonald's 등 글로벌 기업들이 이미 Kotlin Multiplatform을 프로덕션에 활용하고 있습니다. 특히 Cash App은 네트워킹 레이어와 비즈니스 로직 전체를 KMP로 공유하면서 두 플랫폼 간 버그 불일치를 크게 줄였다고 밝힌 바 있습니다. 국내에서도 카카오, 라인 등에서 부분적으로 도입을 검토하거나 적용 중인 것으로 알려져 있습니다.
2. 프로젝트 구조와 시작하기
2-1. 프로젝트 기본 구조 이해하기
KMP 프로젝트는 크게 세 가지 소스 셋으로 나뉩니다. commonMain에는 플랫폼에 독립적인 공통 코드를, androidMain에는 Android 전용 코드를, iosMain에는 iOS 전용 코드를 작성합니다. 플랫폼별로 다르게 동작해야 하는 부분은 expect/actual 메커니즘을 통해 선언과 구현을 분리합니다.
예를 들어 commonMain에서 expect fun getPlatformName(): String으로 선언하면, androidMain에서는 actual fun getPlatformName() = "Android", iosMain에서는 actual fun getPlatformName() = "iOS"로 각각 구현하는 식이죠. 이 구조가 처음엔 낯설 수 있지만, 익숙해지면 꽤 직관적입니다.
2-2. 개발 환경 설정
Android Studio에서 KMP 플러그인을 설치하면 바로 시작할 수 있습니다. JetBrains에서 제공하는 KMP Wizard(kmp.jetbrains.com)를 이용하면 웹에서 프로젝트 템플릿을 생성할 수도 있어서 초기 설정이 한결 편해졌습니다. iOS 빌드를 위해서는 macOS 환경과 Xcode가 필요한데, 이 부분이 Windows나 Linux 사용자에게는 진입 장벽이 될 수 있습니다.
Gradle 설정이 다소 복잡한 편이라 처음 셋업할 때 시간이 좀 걸립니다. 하지만 한번 잡아놓으면 이후에는 크게 손댈 일이 없으니, 초기 셋업에 시간을 충분히 투자하시길 권합니다.
2-3. 핵심 라이브러리 생태계
KMP 생태계는 빠르게 성장하고 있습니다. 주요 라이브러리를 정리하면 다음과 같습니다:
- Ktor — 네트워크 통신 (HTTP 클라이언트)
- SQLDelight — 로컬 데이터베이스 (SQLite 기반)
- Koin / Kotlin Inject — 의존성 주입
- Kotlinx.serialization — JSON 직렬화/역직렬화
- Kotlinx.coroutines — 비동기 처리
- Multiplatform Settings — SharedPreferences/UserDefaults 공유
이 정도면 웬만한 앱의 비즈니스 로직은 충분히 공통 모듈로 작성할 수 있습니다. 다만 아직 서드파티 라이브러리 중 KMP를 지원하지 않는 것들도 있으므로, 프로젝트 시작 전에 필요한 라이브러리의 멀티플랫폼 지원 여부를 꼭 확인하세요.
3. 장단점 솔직 비교
3-1. 다른 크로스플랫폼 프레임워크와 비교
| 항목 | Kotlin Multiplatform | Flutter | React Native |
|---|---|---|---|
| UI 공유 | 네이티브 UI 유지 (선택적 공유) | 완전 공유 (자체 렌더링) | 완전 공유 (브릿지 기반) |
| 네이티브 성능 | 최상 (네이티브 그 자체) | 우수 | 보통 |
| 기존 앱 통합 | 매우 쉬움 (점진적 도입) | 어려움 (모듈 방식 가능) | 보통 |
| 학습 곡선 | Android 개발자: 낮음 / iOS: 높음 | Dart 학습 필요 | JS/React 경험 시 낮음 |
| 코드 공유 범위 | 로직 중심 (50~70%) | 거의 전체 (80~95%) | 거의 전체 (70~90%) |
| 생태계 성숙도 | 성장 중 | 성숙 | 성숙 |
| iOS 개발자 친화성 | 보통 (Swift 연동 필요) | 낮음 | 낮음 |
| 빌드 속도 | 보통 (Gradle 기반) | 빠름 | 빠름 |
3-2. KMP만의 강점과 약점
강점:
- 기존 네이티브 프로젝트에 모듈 하나 추가하는 식으로 점진적 도입이 가능합니다. 전면 재작성이 필요 없다는 건 실무에서 정말 큰 장점이에요.
- 각 플랫폼의 네이티브 UI를 그대로 사용하므로, 플랫폼 가이드라인을 완벽히 준수할 수 있습니다.
- Android 개발자라면 이미 익숙한 Kotlin 언어를 그대로 쓰기 때문에 추가 학습 부담이 적습니다.
- Google이 공식적으로 KMP를 Android 개발 권장 기술로 지정하면서 장기적 지원에 대한 신뢰도가 높아졌습니다.
약점:
- iOS 빌드를 위해 macOS가 반드시 필요합니다. CI/CD 파이프라인 구성 시에도 Mac 러너가 필요하죠.
- Gradle 빌드 설정이 복잡하고, 빌드 시간이 다소 길 수 있습니다.
- iOS 개발자 입장에서는 Kotlin이라는 새로운 언어를 배워야 하는 부담이 있습니다.
- 디버깅 시 iOS 쪽 스택 트레이스가 읽기 어려운 경우가 종종 있습니다.
4. 실전 사용 팁
4-1. 도입 전략: 작게 시작하세요
팁 1: 네트워킹 레이어부터 시작하세요. 처음부터 모든 로직을 공유하려고 하면 부담이 큽니다. 저의 경험상 API 통신 모듈(데이터 모델 + API 클라이언트)을 가장 먼저 공유하는 게 효과가 좋았습니다. 양쪽 플랫폼에서 동일한 API 응답 모델을 사용하게 되면 그것만으로도 버그가 눈에 띄게 줄어듭니다. Ktor + Kotlinx.serialization 조합이면 깔끔하게 구현할 수 있어요.
팁 2: expect/actual은 최소한으로 사용하세요. 플랫폼별 분기가 많아질수록 코드 관리가 복잡해집니다. 가능하면 인터페이스와 의존성 주입 패턴으로 플랫폼 차이를 추상화하고, expect/actual은 정말 필요한 경우에만 쓰는 게 좋습니다. 예를 들어 플랫폼별 UUID 생성이나 파일 경로 같은 아주 기본적인 부분에만 사용하세요.
4-2. 개발 생산성을 높이는 방법
팁 3: iOS 팀과의 소통에 투자하세요. KMP 도입에서 기술적 난이도보다 더 어려운 건 팀 간 협업입니다. iOS 개발자가 공유 모듈의 코드를 읽고 수정할 수 있어야 장기적으로 유지보수가 가능합니다. 공유 모듈의 API를 Swift에서 자연스럽게 사용할 수 있도록 설계하고, iOS 팀이 Kotlin 기본 문법 정도는 이해할 수 있도록 내부 세미나를 진행하는 것을 권합니다.
팁 4: SKIE 라이브러리를 적극 활용하세요. Touchlab에서 만든 SKIE는 KMP에서 생성되는 Objective-C 인터페이스를 Swift 친화적으로 변환해줍니다. sealed class가 Swift의 enum처럼 동작하게 해주고, suspend 함수를 Swift의 async/await로 자연스럽게 사용할 수 있게 해줍니다. iOS 쪽 개발 경험이 확실히 좋아지므로 꼭 도입을 검토해보세요.
4-3. 흔한 실수와 해결법
팁 5: 메모리 관리에 주의하세요. 예전에는 Kotlin/Native의 메모리 모델이 큰 골칫거리였지만, 새로운 메모리 관리자가 도입되면서 많이 개선되었습니다. 그래도 iOS 쪽에서 코루틴을 사용할 때 메모리 누수가 발생하지 않도록 적절한 스코프 관리는 여전히 중요합니다. 공유 모듈에서 CoroutineScope를 노출할 때는 반드시 취소 메커니즘도 함께 제공하세요.
또 하나, Gradle 버전과 Kotlin 버전, 각종 플러그인 버전의 호환성 문제로 빌드가 깨지는 경우가 잦습니다. 버전 업그레이드는 한 번에 하나씩, 그리고 반드시 양쪽 플랫폼 빌드를 모두 확인한 뒤 커밋하는 습관을 들이세요.
5. 마무리: 누구에게 추천하는가
5-1. KMP가 잘 맞는 팀
- 이미 Kotlin으로 Android 앱을 운영 중인 팀 — 가장 자연스러운 전환이 가능합니다.
- 네이티브 UX를 포기할 수 없는 서비스 — 금융, 헬스케어 등 각 플랫폼 가이드라인 준수가 중요한 도메인.
- 점진적 마이그레이션을 원하는 팀 — 기존 앱을 전면 재작성할 여력은 없지만, 코드 중복은 줄이고 싶은 경우.
- 비즈니스 로직의 정합성이 중요한 서비스 — 양쪽 플랫폼에서 동일한 계산 결과, 동일한 유효성 검사가 필요한 경우.
5-2. 다른 선택지가 나을 수 있는 경우
- 빠른 MVP 개발이 목표라면 — Flutter가 UI까지 한 번에 공유할 수 있어서 초기 개발 속도가 더 빠릅니다.
- 웹 프론트엔드 팀이 앱도 담당한다면 — React Native가 기존 역량을 활용하기에 유리합니다.
- iOS만 지원하는 서비스라면 — 굳이 KMP를 도입할 이유가 없겠죠.
Kotlin Multiplatform은 "모든 것을 하나로 통일하자"는 접근이 아니라, "공유할 수 있는 건 공유하고 각자의 강점은 살리자"는 현실적인 철학을 가진 기술입니다. 2024년 Stable 출시 이후로 생태계가 빠르게 안정되고 있고, Google의 공식 지원까지 더해지면서 앞으로의 성장이 더 기대됩니다. 만약 여러분의 팀이 Android와 iOS를 동시에 개발하면서 코드 품질과 네이티브 경험 모두를 중시한다면, KMP는 분명 한번 진지하게 검토해볼 만한 선택지입니다.