최근 드로이드나이츠 앱을 KMP로 마이그레이션 하는 프로젝트를 진행 중이다.
이때 KMP에서는 안드로이드 전용 라이브러리를 그대로 사용할 수 없기 때문에, 기존 Android 코드로로 작성된 부분을 KMP에서도 호환 가능한 라이브러리로 교체해야 한다.
이번 글에서는 그 과정 중 하나로, 의존성 주입 라이브러리를 Hilt에서 Koin으로 마이그레이션 한 과정을 공유해보려고 한다.
Koin
우선 Koin 이란, 모든 Kotlin 애플리케이션(ex. 멀티플랫폼, Android, 백엔드)에 종속성 주입을 쉽고 효율적으로 통합하는 방법을 제공하는 DI 라이브러리이다.
그 외의 특징들로는 아래와 같다.
- 읽기 쉽고, 사용하기 쉬우며, 어떤 종류의 애플리케이션이든 작성할 수 있는 Kotlin DSL이다.
- Android 생태계와는 다른 종류의 통합을 제공하며, Ktor와 같은 더 많은 백엔드 요구 사항에 도달한다.
- 주석과 함께 사용을 허용한다.
Hilt → Koin 마이그레이션
이제 본격적으로 Koin으로 마이그레이션 한 과정을 설명해보겠다.
참고로, 필자는 Koin Annotation을 적용하였다. 그 이유는 어노테이션만으로 의존성 모듈에 객체를 등록할 수 있고, 컴파일 시점에 의존성 검증이 가능하여 안정적이기 때문이다.
더 자세한 이야기는 아래에서 계속 하겠다!
✅1. 의존성 관리
아래의 공식문서를 참고해서 의존성을 넣어주었다.
Koin Annotations | Koin
Setup Koin Annotations for your project
insert-koin.io
Koin Annotation을 사용하기 위해서는 기존 koin 라이브러리에 아래 라이브러리 추가해줘야 한다.
koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin-annotations" }
koin-ksp-compiler = { module = "io.insert-koin:koin-ksp-compiler", version.ref = "koin-annotations" }
그리고 아래처럼 적용해주면 된다. 멀티플랫폼 환경이기에 commonMain 블록 안에 작성을 해 주었다.
kotlin {
sourceSets {
androidMain.dependencies { }
commonMain.dependencies {
api(libs.koin.core)
implementation(libs.koin.compose)
api(libs.koin.annotations)
}
iosMain.dependencies { }
}
}
dependencies {
add("kspCommonMainMetadata", libs.koin.ksp.compiler)
add("kspAndroid", libs.koin.ksp.compiler)
add("kspIosArm64", libs.koin.ksp.compiler)
add("kspIosSimulatorArm64", libs.koin.ksp.compiler)
}
추가로 아래 선언을 통해 컴파일러 플러그인이 컴파일 시 Koin 구성을 확인할 수 있도록 한다.
ksp {
arg("KOIN_CONFIG_CHECK", "true")
arg("KOIN_DEFAULT_MODULE", "false")
}
✅2. 플러그인 만들기
드로이드나이츠의 경우 멀티모듈을 사용하고 있기 때문에 DI가 필요한 모듈마다 위의 의존성을 일일이 넣어주는 것은 불필요하다고 생각했다.
그래서 Koin도 별도의 플러그인 함수로 만들어주고자 했다.
internal fun Project.configureKoinKotlin() {
with(pluginManager) {
apply("com.google.devtools.ksp")
}
dependencies {
"implementation"(libs.findLibrary("koin-core").get())
"implementation"(libs.findLibrary("koin-annotations").get())
"implementation"(libs.findLibrary("koin-compose").get())
"implementation"(libs.findLibrary("koin-compose-viewmodel").get())
"implementation"(libs.findLibrary("koin-compose-viewmodel-navigation").get())
add("ksp", libs.findLibrary("koin-ksp-compiler").get())
}
}
✅3. 본격적인 시작
Hilt에서 Koin으로 마이그레이션 하는 것은 "이론상" 쉽다.
어노테이션들만 바꿔주면 되기 때문이다.
어떤 Koin 어노테이션으로 바꿔야 하는지 알아야 하기 때문에 그 종류를 알아보자.
- @Single: 의존성을 싱글톤으로 설정한다.
- @Factory: 의존성이 주입될 때마다 매번 새로운 인스턴스를 생성한다.
- @KoinViewModel: 안드로이드 ViewModel을 주입할 때 사용한다.
- @Module: 모듈임을 선언한다.
- @ComponentScan: 지정된 패키지 내에서 Koin 어노테이션을 스캔하여 모듈에 등록한다.
이제 이 어노테이션들을 각각 필요한 곳에 잘 배치해주면 된다.
예를 들어, 레포지토리의 경우 객체를 생성해서 주입해주어야 하기 때문에 @Single 어노테이션을 붙여준다.

그럼 기존에 이 레포지토리의 의존성을 주입해줬던 모듈에 @ComponentScan을 추가해준다.

UseCase의 경우, 필요할 때 일회성으로 생성해야 하기 때문에 @Factory 어노테이션으로 등록해준다.

ViewModel은 @HiltViewModel에서 @KoinViewModel로 변경해 준다.

아, 물론 기존 Hilt 어노테이션들은 다 지워줘야 한다!
✅4. Koin 모듈 등록하기
마지막으로 지금까지 생성한 모듈들을 Application의 클래스에 등록해준다.
onCreate()의 startKoin 블록 안에 넣어주면 된다.
class DroidKnightsApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@DroidKnightsApplication)
androidLogger()
modules(
ApiModule().module
)
}
}
}
트러블 슈팅
아마 위처럼 어노테이션만 바꿔줬다면 오류 투성이었을 것이다...
다음은 내가 겪은 트러블 슈팅들이다.
1. abstract 클래스 사용 불가
기존에 @Binds를 사용해 주기 위해 abstract class로 모듈을 작성했었다. 그러나 아래와 같은 에러를 만날 수 있을 것이다.

Hilt와 Koin의 동작 방식이 다르기 때문에 발생한 오류이다.
Koin의 @Module 내 함수는 런타임에 의존성을 등록할 때 실제로 호출을 하는데 abstract class일 경우 인스턴스화할 수 없기 때문이다.
따라서 abstract class를 class로 바꿔줘야 한다.
// 변경 전
@InstallIn(SingletonComponent::class)
@Module
internal abstract class DataModule { }
// 변경 후
@Module
@ComponentScan
class DataModule { }
2. 접근 제어자 변경
Koin은 모듈을 Application 클래스에 등록해줘야 하기 때문에 internal로 작성된 것은 모두 public 접근 제어자로 바꿔줘야 할 것이다.
// 변경 전
internal interface GithubApi { }
// 변경 후
interface GithubApi { }
마치며
이렇게 의존성 주입 라이브러리를 Hilt에서 Koin으로 바꾼 과정을 알아보았다!
Koin과 KMP모두 처음이었기 때문에 시간은 오래 걸렸었지만 마치고 나니 너무 뿌듯했던 작업이었다.
그래서 Hilt와 Koin 중 어느 것이 더 좋았냐고 물어본다면.. 각자의 장단점이 있기 때문에 상황과 필요에 맞춰 적절히 쓰는 것이 좋을 것 같다고 답변을 할 것이다!
다만, 진입장벽은 확실히 Koin이 더 낮다고 느꼈고, Kotlin에 특화된 라이브러리라는 것도 체감할 수 있었다.
한편, 접근제어자를 모두 public으로 바꾸면서 캡슐화를 해치는 단점이 있기 때문에 이러한 특성들을 잘 이해하고 프로젝트 성격에 맞게 신중히 선택하길 권한다!
이 모든 과정이 궁금하시다면 아래 PR로 확인부탁드립니다!
https://github.com/Kotlin-Multiplatform-Laboratory/DroidKnightsApp-KMP/pull/9
[feature/koin] Migrate from Hilt to Koin by leeeyubin · Pull Request #9 · Kotlin-Multiplatform-Laboratory/DroidKnightsApp-KMP
Overview Hilt 에서 Koin으로 마이그레이션 했습니다!
github.com
'Develop > KMP' 카테고리의 다른 글
| [DroidKaigi] CMP로 iOS 첫 빌드 도전기 (컨트리뷰션) (0) | 2025.09.01 |
|---|---|
| [Kalendar] KMP에 Typograpy 적용하기 (3) | 2025.08.18 |
| [DroidKnights] Retrofit -> Ktor 마이그레이션 (컨트리뷰션) (2) | 2025.05.28 |
| [KMP] Kotest로 알아본 Stub vs Fake (테스트 더블) (0) | 2025.03.22 |
| [KMP] Kotest로 테스트 코드 멀티플랫폼에 적용하기 (5) | 2025.03.09 |