Kotlin Multiplatform (KMP): 크로스플랫폼 모바일 개발 심층 가이드
Kotlin Multiplatform (KMP)을 활용한 크로스플랫폼 모바일 앱 개발 가이드입니다. React Native, Flutter와 비교하며 KMP의 장점, 아키텍처, 실전 팁 및 코드 예제를 상세히 다룹니다.
Kotlin Multiplatform (KMP): 크로스플랫폼 모바일 개발 심층 가이드
현대 모바일 애플리케이션 개발은 iOS와 Android라는 두 개의 주요 플랫폼을 동시에 지원해야 하는 복잡성을 안고 있습니다. 이는 종종 중복된 코드베이스, 개발 시간 증가, 그리고 유지보수 비용 상승으로 이어집니다. 이러한 문제점을 해결하기 위해 React Native, Flutter와 같은 크로스플랫폼 프레임워크들이 등장했지만, 이들은 UI까지 공유하는 방식이기에 특정 상황에서는 네이티브 경험을 온전히 제공하기 어렵다는 한계 또한 가지고 있습니다. 이 글에서는 비즈니스 로직은 공유하면서도 UI는 각 플랫폼의 네이티브 경험을 유지할 수 있도록 돕는 혁신적인 기술, Kotlin Multiplatform (KMP)에 대해 심층적으로 알아보겠습니다.
Kotlin Multiplatform (KMP)이란 무엇인가요?
Kotlin Multiplatform (KMP)은 JetBrains에서 개발한 오픈소스 기술로, Kotlin 코드를 다양한 플랫폼(JVM, Android, iOS, JavaScript, WebAssembly 등)에서 실행할 수 있도록 하는 프레임워크입니다. 특히 모바일 개발 영역에서는 Android와 iOS 애플리케이션 간에 비즈니스 로직, 데이터 모델, 네트워크 스택, 데이터베이스 로직 등 핵심 코드를 공유하는 데 초점을 맞춥니다. KMP의 가장 큰 특징은 UI를 공유하지 않고 각 플랫폼의 네이티브 UI 스택(Android의 Jetpack Compose, iOS의 SwiftUI/UIKit)을 그대로 사용한다는 점입니다. 이를 통해 개발자는 코드 재사용의 이점을 누리면서도, 사용자에게는 완벽하게 네이티브스러운 UI/UX 경험을 제공할 수 있습니다.
KMP의 핵심 장점
KMP는 기존 크로스플랫폼 솔루션들과는 다른 독특한 접근 방식으로 여러 가지 강력한 이점을 제공합니다.
1. 효율적인 코드 재사용성
가장 명백한 장점은 비즈니스 로직, 데이터 모델, 네트워킹 코드, 유틸리티 함수 등 앱의 핵심 로직을 한 번 작성하여 Android와 iOS에서 모두 사용할 수 있다는 점입니다. 이는 개발 시간을 단축하고, 버그 발생률을 줄이며, 일관된 동작을 보장합니다.
2. 완벽한 네이티브 성능 및 UI/UX
KMP는 UI를 직접 렌더링하지 않고, 각 플랫폼의 네이티브 UI 프레임워크를 사용합니다. 즉, Android 앱은 Kotlin/Jetpack Compose 또는 XML로, iOS 앱은 Swift/SwiftUI 또는 UIKit으로 UI를 구축합니다. 이를 통해 앱은 각 플랫폼의 디자인 가이드라인을 완벽하게 따르며, 최적화된 성능과 네이티브스러운 사용자 경험을 제공할 수 있습니다.
3. 점진적 도입 및 유연한 통합
KMP는 "올인원" 솔루션을 강요하지 않습니다. 기존에 이미 개발된 네이티브 Android 또는 iOS 프로젝트에 필요한 부분부터 KMP 모듈을 추가하여 점진적으로 코드를 공유할 수 있습니다. 이는 개발팀이 전체 앱을 한 번에 전환할 필요 없이, 가장 필요한 부분부터 KMP의 이점을 활용할 수 있게 해줍니다.
4. Kotlin 언어의 강력함
Kotlin은 현대적이고 간결하며 안전한 언어로, Java와의 완벽한 호환성을 제공합니다. KMP는 Kotlin의 강력한 기능(코루틴, 확장 함수, Null Safety 등)을 활용하여 생산성을 높이고, 개발자 경험을 향상시킵니다.
5. 개발 생산성 향상
단일 언어(Kotlin)와 단일 IDE(IntelliJ IDEA 또는 Android Studio)를 사용하여 여러 플랫폼을 동시에 개발할 수 있어, 개발팀의 생산성을 크게 향상시킬 수 있습니다. 이는 학습 곡선을 줄이고, 팀원 간의 협업을 더욱 원활하게 만듭니다.
다른 크로스플랫폼 프레임워크와의 비교: KMP vs. React Native, Flutter
KMP는 React Native, Flutter와 같은 기존의 크로스플랫폼 프레임워크들과는 다른 철학을 가지고 있습니다. 다음 표를 통해 주요 차이점을 비교해 보겠습니다.
| 특징 | Kotlin Multiplatform (KMP) | React Native | Flutter |
|---|---|---|---|
| 코드 공유 범위 | 비즈니스 로직, 데이터, 네트워크 등 (UI 제외) | UI 및 비즈니스 로직 (JavaScript) | UI 및 비즈니스 로직 (Dart) |
| UI 렌더링 | 각 플랫폼의 네이티브 UI 스택 사용 (Jetpack Compose, SwiftUI/UIKit) | JavaScript 브릿지를 통해 네이티브 컴포넌트 렌더링 | 자체 렌더링 엔진(Skia)을 통해 UI 직접 그림 |
| 언어 | Kotlin | JavaScript / TypeScript | Dart |
| 성능 | 네이티브에 준하는 성능 (UI는 순수 네이티브) | 브릿지 오버헤드 존재 가능, 최적화 필요 | 고성능, 자체 렌더링 엔진으로 네이티브에 준함 |
| 네이티브 접근 | expect/actual 메커니즘으로 유연하게 접근 | Native Modules/JSI를 통해 접근 | Platform Channels를 통해 접근 |
| 점진적 도입 | 매우 용이 (기존 네이티브 앱에 모듈로 추가) | 가능하지만, UI 프레임워크의 특성상 통합에 고려 필요 | 위젯 기반으로 통합이 복잡할 수 있음 |
| 주요 장점 | 네이티브 UI/UX 유지, 로직 재사용, 점진적 도입 | 빠른 개발 속도, 웹 개발자에게 친숙, 활발한 커뮤니티 | 아름다운 UI, 높은 성능, 단일 코드베이스, 생산성 |
| 주요 단점 | UI 개발은 여전히 분리, 생태계 성장 중 | 브릿지 오버헤드, 네이티브 모듈 의존성, 런타임 성능 이슈 | 앱 크기, 네이티브 모듈 의존성, Dart 학습 필요 |
KMP는 네이티브 앱의 장점을 최대한 유지하면서 코드 재사용의 이점을 얻고자 할 때, 특히 복잡한 비즈니스 로직을 가진 앱에 이상적인 선택이 될 수 있습니다.
KMP 아키텍처 이해하기: common, android, ios 모듈
KMP 프로젝트는 일반적으로 여러 모듈로 구성되며, 이 모듈들은 코드가 실행될 플랫폼에 따라 나뉩니다. 가장 기본적인 구조는 다음과 같습니다.
-
commonMain: 플랫폼에 독립적인 코드를 작성하는 곳입니다. 비즈니스 로직, 데이터 모델, 네트워크 인터페이스, 유틸리티 함수 등이 여기에 포함됩니다. 이 코드는 모든 대상 플랫폼에서 공유됩니다. -
androidMain: Android 플랫폼에 특화된 코드를 작성하는 곳입니다.commonMain에서expect로 선언된 기능에 대한actual구현, Android SDK를 사용하는 코드 등이 포함됩니다. -
iosMain: iOS 플랫폼에 특화된 코드를 작성하는 곳입니다.commonMain에서expect로 선언된 기능에 대한actual구현, iOS 프레임워크를 사용하는 코드 등이 포함됩니다.
KMP의 핵심 메커니즘 중 하나는 expect와 actual 키워드입니다. expect는 commonMain에서 특정 기능(클래스, 함수, 속성 등)의 선언만 하고, 각 플랫폼별 모듈에서 actual 구현을 제공하도록 강제합니다. 이를 통해 플랫폼별로 다른 동작이 필요한 경우에도 commonMain에서는 추상화된 인터페이스를 통해 일관된 코드를 작성할 수 있습니다.
예를 들어, 현재 실행 중인 플랫폼 이름을 가져오는 함수를 구현해 보겠습니다.
// commonMain/kotlin/com/example/shared/Platform.kt
package com.example.shared
expect class Platform {
val name: String
}
fun getPlatformName(): String = Platform().name
이제 각 플랫폼에서 Platform 클래스의 actual 구현을 제공해야 합니다.
// androidMain/kotlin/com/example/shared/Platform.kt
package com.example.shared
actual class Platform actual constructor() {
actual val name: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}
// iosMain/kotlin/com/example/shared/Platform.kt
package com.example.shared
import platform.UIKit.UIDevice
actual class Platform actual constructor() {
actual val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
이렇게 하면 commonMain의 getPlatformName() 함수를 호출했을 때, Android에서는 "Android 33"과 같은 값을, iOS에서는 "iOS 17.0"과 같은 값을 얻게 됩니다.
실전 KMP 개발 팁
KMP 프로젝트를 효율적으로 개발하기 위한 몇 가지 실전 팁을 공유합니다.
1. 데이터 모델 공유
commonMain에 data class를 정의하여 API 응답, 앱 내부 데이터 구조 등을 공유합니다. kotlinx.serialization 라이브러리를 사용하면 JSON 직렬화/역직렬화를 플랫폼 독립적으로 처리할 수 있습니다.
// commonMain/kotlin/com/example/shared/model/User.kt
package com.example.shared.model
import kotlinx.serialization.Serializable
@Serializable
data class User(
val id: String,
val name: String,
val email: String
)
2. 네트워크 레이어 구축
Ktor HTTP 클라이언트는 KMP를 완벽하게 지원하며, commonMain에서 네트워크 요청 로직을 작성할 수 있게 해줍니다. kotlinx.serialization과 함께 사용하여 API 통신을 효과적으로 구현할 수 있습니다.
// commonMain/kotlin/com/example/shared/network/ApiClient.kt
package com.example.shared.network
import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
class ApiClient {
private val httpClient = HttpClient {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
prettyPrint = true
isLenient = true
})
}
}
suspend fun getUsers(): String {
return httpClient.get("https://jsonplaceholder.typicode.com/users").bodyAsText()
}
}
3. 상태 관리 패턴 활용
KMP에서는 MVI(Model-View-Intent)와 같은 단방향 데이터 흐름 패턴을 적용하기 용이합니다. kotlinx.coroutines의 Flow를 사용하여 비동기 데이터 스트림을 처리하고, StateFlow나 SharedFlow를 통해 UI 상태를 공유할 수 있습니다.
4. 의존성 주입 (DI)
Koin이나 Kodein과 같은 Kotlin 기반 DI 프레임워크는 KMP를 지원합니다. commonMain에서 의존성 선언을 하고, 각 플랫폼에서 필요한 모듈을 로드하여 사용할 수 있습니다.
5. iOS 통합: CocoaPods 또는 Swift Package Manager (SPM)
KMP 모듈을 iOS 프로젝트에 통합하는 방법은 크게 두 가지입니다.
- CocoaPods:
build.gradle.kts파일에 CocoaPods 플러그인을 설정하고,pod install을 통해 iOS 프로젝트에 프레임워크로 추가할 수 있습니다. - Swift Package Manager (SPM): 최근 KMP는 SPM을 통한 통합도 공식적으로 지원하고 있어, iOS 개발자들이 더욱 편리하게 KMP 모듈을 사용할 수 있게 되었습니다.
6. 테스트 전략
commonTest, androidTest, iosTest 모듈을 활용하여 공유 로직은 commonTest에서, 플랫폼별 구현은 각 플랫폼의 테스트 모듈에서 작성하여 견고한 테스트 커버리지를 확보합니다.
간단한 KMP 프로젝트 시작하기
KMP 프로젝트를 시작하는 가장 쉬운 방법은 Android Studio의 KMP 템플릿을 활용하는 것입니다.
- Android Studio를 열고 "New Project"를 선택합니다.
- "Kotlin Multiplatform Library" 또는 "Kotlin Multiplatform Application" 템플릿을 선택합니다. (Application 템플릿이 더 많은 설정이 포함되어 편리합니다.)
- 프로젝트 이름과 패키지 이름을 설정하고 "Finish"를 누릅니다.
템플릿은 기본적으로 shared 모듈과 androidApp, iosApp 모듈을 생성해 줍니다. shared 모듈 내부에 commonMain, androidMain, iosMain 디렉토리가 구성됩니다.
shared/src/commonMain/kotlin/com/example/Greeting.kt 파일을 열면 다음과 같은 코드를 볼 수 있습니다.
package com.example
class Greeting {
private val platform: Platform = getPlatform()
fun greet(): String {
return "Hello, ${platform.name}!"
}
}
여기서 getPlatform() 함수는 expect 함수로 선언되어 있고, platform.name은 각 플랫폼의 actual 구현에서 가져옵니다.
Android 앱에서 호출:
androidApp/src/main/java/com/example/android/MainActivity.kt 파일에서 Greeting 클래스를 사용하여 메시지를 표시할 수 있습니다.
package com.example.android
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable관련 게시글
Kotlin Multiplatform으로 Cross-Platform 모바일 개발 가속화하기
Kotlin Multiplatform (KMP)을 활용한 크로스플랫폼 모바일 앱 개발 전략을 심층적으로 다룹니다. iOS와 Android에서 코드 재사용을 극대화하고, 네이티브 UI와 성능을 유지하는 방법을 알아보세요.
SwiftUI 입문 가이드: Cross-Platform 개발자를 위한 iOS 앱 여정 시작하기
React Native, Flutter 개발자도 쉽게 시작할 수 있는 SwiftUI 입문 가이드! 선언형 UI, 상태 관리, 핵심 컴포넌트, 그리고 실전 To-Do 앱 예제까지, iOS 모바일 앱 개발의 첫걸음을 떼어보세요.
SwiftUI Introduction: Building Modern iOS and Cross-Platform Apps
SwiftUI의 기본부터 심화 개념, 그리고 React Native, Flutter와 같은 크로스 플랫폼 프레임워크와의 비교를 통해 현대 모바일 앱 개발에 SwiftUI를 활용하는 방법을 상세히 다룹니다.