첫 번째 KMP 프로젝트 구성: Kotlin 다중 플랫폼을 사용한 Android, iOS 및 데스크톱
Kotlin 다중 플랫폼 프로젝트를 처음부터 설정하는 것은 전체 KMP 여정에서 가장 기술적인 단계 중 하나입니다.
Flutter와 달리 flutter create 필요한 모든 것을 생산하거나 React Native에서
Expo는 복잡성을 처리하고 KMP는 Gradle, 소스 세트 구조,
공유 논리의 첫 번째 줄을 작성하기 전의 예상/실제 메커니즘입니다.
이 가이드는 빈 프로젝트 생성부터 코드를 사용하여 작동하는 애플리케이션까지 안내합니다. Android와 iOS 간에 공유되며 자세한 설명과 함께 각 구성 단계를 보여줍니다. 마지막에는 올바른 구조, 구성된 Gradle 버전 카탈로그 및 첫 번째 프로젝트를 갖게 됩니다. 예상/실제 기능이 작동합니다.
전제 조건
- KMP 플러그인이 설치된 Android Studio Hedgehog(2023.1.1) 이상
- Xcode 15+(iOS 앱 빌드용 - macOS에서만)
- JAVA_HOME으로 구성된 JDK 17+
- Kotlin 및 Gradle에 대한 기본 지식
1단계: KMP 마법사를 사용하여 프로젝트 생성
시작하는 가장 빠른 방법은 다음을 사용하는 것입니다. Kotlin 다중 플랫폼 마법사 사용 가능
주소에 kmp.jetbrains.com 또는 다음을 통해 Android Studio에서 직접
파일 → 새로 만들기 → 새 프로젝트 → Kotlin Multiplatform App.
# Alternativa da riga di comando con il KMP Wizard CLI (2026)
# Installa il plugin KMP di IntelliJ/Android Studio dal marketplace
# oppure usa il sito web:
# 1. Vai su https://kmp.jetbrains.com
# 2. Configura: nome progetto, package, target (Android, iOS, Desktop)
# 3. Scarica il progetto e aprilo in Android Studio
# Struttura generata dal wizard:
my-kmp-app/
├── composeApp/ # (se scegli Compose Multiplatform)
│ └── build.gradle.kts
├── shared/ # Il modulo condiviso principale
│ ├── src/
│ │ ├── commonMain/
│ │ ├── androidMain/
│ │ └── iosMain/
│ └── build.gradle.kts
├── androidApp/ # App Android standalone
│ └── build.gradle.kts
├── iosApp/ # App iOS standalone (Swift)
│ └── iosApp.xcodeproj
├── gradle/
│ └── libs.versions.toml # Version catalog
├── build.gradle.kts # Root build script
└── settings.gradle.kts
2단계: 버전 카탈로그(libs.versions.toml)
Il Gradle 버전 카탈로그 모든 버전 관리를 중앙 집중화합니다. 단일 TOML 파일의 프로젝트 종속성. JetBrains에서 권장하는 모범 사례입니다. KMP 프로젝트의 경우:
# gradle/libs.versions.toml
[versions]
kotlin = "2.0.20"
kotlinx-coroutines = "1.9.0"
kotlinx-serialization = "1.7.3"
ktor = "2.3.12"
sqldelight = "2.0.2"
koin = "3.5.6"
agp = "8.5.2"
compose-multiplatform = "1.7.0"
kotlinx-datetime = "0.6.1"
[libraries]
# Kotlin
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
# Coroutines
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
# Serialization
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
# Ktor
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
# SQLDelight
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqldelight" }
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "sqldelight" }
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
# Koin DI
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
# DateTime
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
3단계: 공유 모듈 빌드 스크립트
Il build.gradle.kts 공유 모듈 중 가장 중요한 구성 파일입니다.
프로젝트의. 각각의 빌드 대상, 소스 세트 및 종속성을 정의합니다.
// shared/build.gradle.kts
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.sqldelight)
}
kotlin {
// Target Android
androidTarget {
compilations.all {
kotlinOptions {
jvmTarget = "17"
}
}
}
// Target iOS (arm64 per device, x64 per simulator Intel, simulatorArm64 per M1/M2)
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "Shared"
isStatic = true
}
}
// Target Desktop JVM (opzionale)
jvm("desktop")
sourceSets {
// Codice comune a tutte le piattaforme
commonMain.dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.json)
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutines)
implementation(libs.koin.core)
implementation(libs.kotlinx.datetime)
}
// Test comuni
commonTest.dependencies {
implementation(libs.kotlin.test)
}
// Android-specific
androidMain.dependencies {
implementation(libs.ktor.client.okhttp)
implementation(libs.sqldelight.android.driver)
implementation(libs.kotlinx.coroutines.android)
}
// iOS-specific
val iosMain by getting {
// Su iOS non puoi usare "iosMain" direttamente se hai piu target iOS
// Usa una convenzione condivisa per i tre target iOS
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
// Source set condiviso per tutti i target iOS
create("iosMain") {
dependsOn(commonMain.get())
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation(libs.ktor.client.darwin)
implementation(libs.sqldelight.native.driver)
}
}
}
}
android {
namespace = "com.example.mykmpapp.shared"
compileSdk = 35
defaultConfig {
minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
sqldelight {
databases {
create("AppDatabase") {
packageName.set("com.example.mykmpapp.db")
}
}
}
4단계: 소스 세트 이해
I 소스 세트 이것이 KMP의 기본 구조입니다. 각 소스 세트는 하나입니다.
종속성이 있는 Kotlin 코드 디렉터리 및 소스 세트는 다른 디렉터리에 종속될 수 있습니다.
계층 구조를 통해 dependsOn:
// Gerarchia dei source set tipica
commonMain
├── androidMain (usa librerie Android: OkHttp, Android SQLite)
├── iosMain (usa librerie Darwin: URLSession, iOS SQLite)
│ ├── iosX64Main
│ ├── iosArm64Main
│ └── iosSimulatorArm64Main
└── desktopMain (usa librerie JVM: OkHttp, SQLite JDBC)
// Tutti i source set "eredita" le dipendenze di commonMain
// androidMain puo usare tutto di commonMain + dipendenze Android-specific
프로젝트 파일 시스템에서 디렉터리 구조는 소스 세트를 반영합니다.
shared/src/
├── commonMain/
│ └── kotlin/
│ └── com/example/mykmpapp/
│ ├── data/
│ ├── domain/
│ └── Platform.kt # expect declaration
├── androidMain/
│ └── kotlin/
│ └── com/example/mykmpapp/
│ └── Platform.android.kt # actual per Android
└── iosMain/
└── kotlin/
└── com/example/mykmpapp/
└── Platform.ios.kt # actual per iOS
5단계: 첫 번째 기대/실제 함수
메커니즘 예상/실제 KMP가 플랫폼별 차이점을 처리하는 방법을 알아보세요.
expect 공통 코드로 API를 선언하고, actual 각각에 대해 구현합니다.
플랫폼. 현재 플랫폼에 대한 정보를 얻는 전형적인 예부터 시작해 보겠습니다.
// commonMain/kotlin/com/example/mykmpapp/Platform.kt
expect class PlatformInfo() {
val name: String
val version: String
val isDebug: Boolean
}
// Funzione che usa l'implementazione platform-specific
fun greeting(): String = "Running on ${PlatformInfo().name} ${PlatformInfo().version}"
// androidMain/kotlin/com/example/mykmpapp/Platform.android.kt
import android.os.Build
actual class PlatformInfo {
actual val name: String = "Android ${Build.VERSION.RELEASE}"
actual val version: String = Build.VERSION.SDK_INT.toString()
actual val isDebug: Boolean = BuildConfig.DEBUG
}
// iosMain/kotlin/com/example/mykmpapp/Platform.ios.kt
import platform.UIKit.UIDevice
actual class PlatformInfo {
actual val name: String = UIDevice.currentDevice.systemName()
actual val version: String = UIDevice.currentDevice.systemVersion
actual val isDebug: Boolean = Platform.isDebugBinary
}
두 번째로 실용적인 예: UUID 생성(플랫폼별로 서로 다른 API 사용):
// commonMain: dichiarazione expect
expect fun generateUUID(): String
// androidMain: implementazione con java.util.UUID
actual fun generateUUID(): String = java.util.UUID.randomUUID().toString()
// iosMain: implementazione con NSUUID di iOS
import platform.Foundation.NSUUID
actual fun generateUUID(): String = NSUUID().UUIDString()
6단계: Android 앱 구성
Android 앱은 공유 모듈에 의존하는 표준 Gradle 모듈입니다. 구성 그리고 간단하다:
// androidApp/build.gradle.kts
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.example.mykmpapp.android"
compileSdk = 35
defaultConfig {
applicationId = "com.example.mykmpapp"
minSdk = 26
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
buildFeatures {
compose = true
}
}
dependencies {
// Dipende dal modulo shared
implementation(projects.shared)
// UI Android (Compose o View-based)
implementation(platform("androidx.compose:compose-bom:2026.01.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
implementation("androidx.activity:activity-compose:1.9.3")
}
7단계: iOS 앱 구성
iOS를 통합하려면 컴파일된 KMP 프레임워크를 포함하도록 Xcode를 구성해야 합니다.
공유 모듈은 iOS 프레임워크(Shared.xcframework) 그
Xcode 앱에 연결되어 있습니다:
# Script di build iOS da aggiungere come Xcode Build Phase:
# "Run Script" - da aggiungere in Build Phases di Xcode
cd "$SRCROOT/.."
# Compila il framework KMP per il target iOS corrente
if [ "$PLATFORM_NAME" = "iphonesimulator" ]; then
if [ "$ARCHS" = "arm64" ]; then
TARGET="iosSimulatorArm64"
else
TARGET="iosX64"
fi
else
TARGET="iosArm64"
fi
./gradlew "shared:link${TARGET}DebugFrameworkIos${TARGET^}" \
-Pkotlin.native.useEmbeddableCompilerJar=true
# Il framework viene copiato automaticamente da Gradle
iOS 앱의 Swift 파일에서 프레임워크는 일반 Swift 프레임워크처럼 가져옵니다.
// iosApp/ContentView.swift
import SwiftUI
import Shared // Il framework KMP compilato
struct ContentView: View {
@State private var greeting = ""
var body: some View {
VStack {
Text(greeting)
.padding()
Button("Refresh") {
greeting = Greeting().greeting()
}
}
.onAppear {
// Chiama il codice Kotlin dal framework condiviso
greeting = PlatformInfoKt.greeting()
}
}
}
8단계: 공유 모듈의 첫 번째 테스트
공유 모듈에서 단위 테스트를 작성하고 실행하여 모든 것이 작동하는지 확인합니다.
공통주요 용도 테스트 kotlin.test 모든 플랫폼에서 작동합니다.
// commonTest/kotlin/com/example/mykmpapp/PlatformTest.kt
import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class PlatformTest {
@Test
fun testPlatformInfoNotNull() {
val info = PlatformInfo()
assertNotNull(info.name)
assertNotNull(info.version)
}
@Test
fun testGreetingContainsPlatform() {
val greet = greeting()
assertTrue(greet.contains("Running on"), "Greeting dovrebbe contenere 'Running on'")
}
@Test
fun testUUIDFormat() {
val uuid = generateUUID()
// UUID formato: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
assertTrue(uuid.length == 36, "UUID dovrebbe avere 36 caratteri")
assertTrue(uuid.count { it == '-' } == 4, "UUID dovrebbe avere 4 trattini")
}
}
# Esegui i test sul target JVM (piu veloce, per CI)
./gradlew shared:jvmTest
# Esegui i test su Android (emulatore o device)
./gradlew shared:connectedAndroidTest
# Esegui i test iOS (solo su macOS)
./gradlew shared:iosSimulatorArm64Test
일반적인 문제 및 해결 방법
- 'Kotlin/네이티브 툴체인을 찾을 수 없음' 오류: 당신이 가지고 있는지 확인하십시오 JAVA_HOME으로 구성된 JDK 17+ 및 해당 Gradle이 툴체인을 다운로드했습니다. Kotlin/Native(이러한 이유로 첫 번째 빌드가 더 느립니다).
-
"실제 선언을 찾을 수 없습니다." 오류: 당신은 사용하고 있습니다
expect해당하는 것을 제공하지 않고actual에 대한 구성된 모든 대상. 실제 클래스/함수가 존재하는지 확인하세요. 구성된 모든 소스 세트에서kotlin { }. - 오래된 iOS 프레임워크: Kotlin 코드를 변경한 후 Xcode 빌드를 정리하고(Cmd+Shift+K) 다시 컴파일하세요. 프레임워크는 다음과 같아야 합니다. Xcode에서 볼 수 있기 전에 Gradle로 다시 컴파일해야 합니다.
- 라이브러리 호환성: 모든 Java 라이브러리가 작동하는 것은 아닙니다. iOS. README에서 항상 'KMP' 또는 'Kotlin Multiplatform' 태그가 있는 라이브러리를 사용하세요.
최종 프로젝트의 구조
이 구성이 끝나면 프로젝트의 운영 구조는 다음과 같습니다.
my-kmp-app/
├── gradle/
│ └── libs.versions.toml # Version catalog centralizzato
├── shared/ # Modulo KMP condiviso
│ ├── src/
│ │ ├── commonMain/kotlin/ # Logica condivisa
│ │ ├── commonTest/kotlin/ # Test condivisi
│ │ ├── androidMain/kotlin/ # Android-specific
│ │ └── iosMain/kotlin/ # iOS-specific
│ └── build.gradle.kts
├── androidApp/ # App Android
│ ├── src/main/...
│ └── build.gradle.kts
├── iosApp/ # App iOS (Xcode project)
│ ├── iosApp/
│ │ ├── ContentView.swift
│ │ └── iOSApp.swift
│ └── iosApp.xcodeproj
├── build.gradle.kts # Root (plugin declarations)
└── settings.gradle.kts # Moduli inclusi
결론 및 다음 단계
Android, iOS 및 공유 모듈을 사용하여 완전한 KMP 프로젝트를 설정했습니다. 곡선 Gradle과 소스 세트의 학습 곡선은 처음에는 가파르지만, 엔터프라이즈 프로젝트를 위해 만들어지고 강력하며 확장 가능합니다.
다음 기사에서는공유 모듈 아키텍처: 더 복잡한 패턴에 대해 예상/실제를 사용하는 방법, 종속성을 위해 Koin을 설정하는 방법 멀티플랫폼 주입 및 쉽게 테스트할 수 있도록 코드를 구성하는 방법 고립되어 있습니다.
시리즈: Kotlin Multiplatform — 하나의 코드베이스, 모든 플랫폼
- 기사 1: 2026년 KMP — 아키텍처, 구축 및 생태계
- 제2조(본): 첫 번째 KMP 프로젝트 구성 — Android, iOS 및 데스크톱
- 기사 3: 공유 모듈 아키텍처 - 예상/실제, 인터페이스 및 DI
- 기사 4: Ktor 클라이언트와의 멀티플랫폼 네트워킹
- 기사 5: SQLDelight를 사용한 다중 플랫폼 지속성
- 기사 6: Compose 다중 플랫폼 — Android 및 iOS의 공유 UI
- 기사 7: 상태 관리 KMP — ViewModel 및 Kotlin 흐름
- 기사 8: KMP 테스트 - 단위 테스트, 통합 테스트 및 UI 테스트
- 기사 9: Swift 내보내기 — iOS와의 관용적 상호 운용성
- 기사 10: KMP 프로젝트를 위한 CI/CD — GitHub Actions 및 Fastlane
- 기사 11: 사례 연구 — KMP를 활용한 핀테크 앱 제작







