kotlin을 활용해 멀티플랫폼을 구현한다는 새로운 것에 도전하는 개발자 꿈나무 김조현입니다.
이번 글에서는 "여백"이라는 온라인 교환독서 프로젝트를 진행하면서 알게된 플랫폼별 추상화에 사용되는 expect, actual에 대해 소개해보겠습니다.
expect / actual 은 KMP에서 플랫폼별 구현 차이를 분리하기 위해 사용하는 문법이다.
expect : 공통 코드에서 "이 기능이 존재할 것"이라고 선언
actual : 각 플랫폼에서 그 기능을 실제로 구현
KMP 프로젝트는 androidMain, iosMain, commonMain 보통 이렇게 나뉜다. commonMain에서는 Android나 iOS API를 직접 사용할 수 없다.
예를 들어 "여백" 앱을 구현할 때 텍스트 복사에 사용된 ClipData.newPlainText()는 Android API다. 이를 commonMain에서 사용할 수 없다.
InviteCodeCard(
groupCode = uiState.groupUiModel.groupCode,
onClick = {
coroutineScope.launch {
clipboard.setClipEntry(ClipData.newPlainText())
// commonMain에서 androidAPI를 사용할 수 없다.
}
},
modifier = Modifier.padding(horizontal = 16.dp),
)
이런 문제를 해결하기 위해서는 플랫폼 별 다른 방식을 구현해야 한다. 이 때 사용되는 것이 expect / actual이다.
expect를 다음과 같이 선언할 수 있다.
expect fun String.toClipEntry(): ClipEntry
expect는 구현부가 없다. 위에서 말했듯이 commonMain에서는 각 플랫폼이 어떻게 구현하는지 모르고, API의 형태만 선언하고 사용하며, 이를 구현해주는 것이 actual이다.
actual은 expect에서 선언한 추상화를 실제로 구현하는 곳이다. 아래처럼 android, iOS에서 이렇게 각각 구현해주는 것이다.
android에서의 toClipEntry 구현
actual fun String.toClipEntry(): ClipEntry {
val clipData = ClipData.newPlainText(this, this)
return ClipEntry(clipData)
}
iOS에서의 toClipEntry 구현
actual fun String.toClipEntry(): ClipEntry = ClipEntry.withPlainText(this)
이렇게 하면 commonMain에서 각각 네이티브 API를 활용할 수 있다.
commonMain
expect fun String.toClipEntry()
│
│
"플랫폼별 구현 필요"
│
┌─────────┴─────────┐
▼ ▼
androidMain iosMain
actual fun actual fun
toClipEntry() toClipEntry()
│ │
▼ ▼
ClipData iOS 방식
interface와 비슷하게 expect / actual도 구현과 사용을 분리한다는 공통점이 있다. 하지만 두 키워드의 목적이 다르다.
interface | expect / actual | |
|---|---|---|
| 목적 | 객체의 구현 교체 | 플랫폼별 구현 분리 |
| 구현 선택 | 런타임에도 가능 | 빌드 대상 플랫폼에 따라 결정 |
| Android/iOS 분리 | 가능하지만 직접 설계 필요 | 이를 위한 언어 기능 |
| 일반 Kotlin 프로젝트 | ✅ | 보통 ❌ |
| Kotlin Multiplatform | ✅ | ✅ |
| DI와 조합 | 매우 자주 사용 | 가능 |
아니다. 함수 외에도 클래스, 프로퍼티 등에 사용할 수도 있다.
expect fun getPlatformName(): String
expect class PlatformLogger {
fun log(message: String)
}
expect val platformName: String
상황에 따라 사용할 수 있지만 expect / actual의 본질은 공통 코드에서 API의 모양만 정의하고, 구현은 플랫폼이 제공한다. 이다.
expect / actual은 KMP 프로젝트에서 공통 코드가 필요로 하는 기능의 API를 선언하고, 플랫폼마다 구현 방법이 다른 부분을 각 플랫폼 별 코드로 분리하기 위해 사용된다.
expect / actual은 얼핏 보면 interface와 비슷해보이지만 사용하는 목적, 의도가 다르다.
expect / actual의 본질은 공통 코드에서 API의 모양만 정의하고, 구현은 플랫폼이 제공한다. 이다.