[KMP] expect / actual이 뭐야?

왕왕조현·2026년 8월 29일
post-thumbnail

안녕하세요!

kotlin을 활용해 멀티플랫폼을 구현한다는 새로운 것에 도전하는 개발자 꿈나무 김조현입니다.

이번 글에서는 "여백"이라는 온라인 교환독서 프로젝트를 진행하면서 알게된 플랫폼별 추상화에 사용되는 expect, actual에 대해 소개해보겠습니다.

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를 다음과 같이 선언할 수 있다.

expect fun String.toClipEntry(): ClipEntry

expect는 구현부가 없다. 위에서 말했듯이 commonMain에서는 각 플랫폼이 어떻게 구현하는지 모르고, API의 형태만 선언하고 사용하며, 이를 구현해주는 것이 actual이다.

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랑 뭐가 다른가?

interface와 비슷하게 expect / actual도 구현과 사용을 분리한다는 공통점이 있다. 하지만 두 키워드의 목적이 다르다.

  1. interface는 객체의 구현을 교체하여 의존성을 낮추기 위해 사용된다. expect / actual은 플랫폼 별 구현을 분리하기 위해 사용된다.
  2. interface는 런타임에서도 객체의 변경이 가능하다. 하지만 expect / actual은 컴파일 시점에서 확인하기 때문에 빌드 후에 바뀌지 않는다.
  3. expect / actual은 일반 코틀린 프로젝트에서 사용할 수 없다. KMP 프로젝트에서 사용할 수 있다.
interfaceexpect / actual
목적객체의 구현 교체플랫폼별 구현 분리
구현 선택런타임에도 가능빌드 대상 플랫폼에 따라 결정
Android/iOS 분리가능하지만 직접 설계 필요이를 위한 언어 기능
일반 Kotlin 프로젝트✅보통 ❌
Kotlin Multiplatform✅✅
DI와 조합매우 자주 사용가능

expect / actual은 함수에만 사용할 수 있는가?

아니다. 함수 외에도 클래스, 프로퍼티 등에 사용할 수도 있다.

함수 사용

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의 모양만 정의하고, 구현은 플랫폼이 제공한다. 이다.

profile
천천히, 꾸준히, 한 걸음씩

0개의 댓글