Koin은 코드 생성, 리플렉션 또는 어노테이션 없이 작동하는 Kotlin용 DI 프레임워크입니다. 이 라이브러리는 DSL을 사용하여 모듈을 설명하고 Android, Ktor 및 Multiplatform을 지원하는 경량 컨테이너를 통해 종속성을 주입합니다. 공식 Koin 문서에 따르면, 프레임워크는 최소한의 boilerplate로 모듈, 스코프 및 Jetpack Compose에 대한 내장 지원을 제공합니다.
핵심 사항
Koin은 리플렉션, 어노테이션 또는 코드 생성 없이 순수 언어로 작성된 Kotlin용 DI 프레임워크입니다. 컴파일 타임에 어노테이션 프로세서와 코드 생성이 필요한 Dagger Hilt와 달리, Koin은 모듈을 설명하는 경량 DSL을 사용하여 런타임에서만 작동합니다.
Koin의 주요 아이디어는 복잡한 종속성 그래프와 구성 요소 트리 개념을 배울 필요 없이 종속성을 등록하고 해결하기 위한 간단한 API를 제공하는 것입니다. 개발자는 컨테이너에서 사용할 수 있는 클래스를 설명하고, Koin은 생성자 또는 by inject를 통한 지연 델리게이트를 통해 자동으로 주입합니다. 이 프레임워크는 Kotlin Multiplatform과 완벽하게 호환되므로 Android, iOS 및 서버 측에서 통합된 DI 접근 방식을 사용할 수 있습니다.
Kotlin Developers Community 설문 조사(2025)에 따르면, Koin은 상용 Android 프로젝트의 31%에서 사용되며 Hilt(47%)에 이어 두 번째입니다. 선택하는 주된 이유는 설정의 용이성과 코드 생성이 필요하지 않아 프로젝트 빌드 속도가 빨라지기 때문입니다.
빠른 개발 시작이 중요한 중대형 프로젝트나 아키텍처상의 이유로 Hilt를 사용할 수 없는 Kotlin Multiplatform 솔루션에는 Koin을 선택하세요.
Koin은 리플렉션이나 코드 생성을 사용하지 않습니다. 모든 등록은 컴파일 타임에 함수 본문에 구체적인 유형을 대체하는 reified 유형의 인라인 함수를 기반으로 합니다. 이로 인해 Koin은 최종 APK 크기 측면에서 가장 가벼운 DI 프레임워크 중 하나입니다. Koin을 추가하면 애플리케이션 크기가 100-150KB만 증가하는 반면, Dagger Hilt는 생성된 코드로 인해 약 500KB가 증가합니다.
Koin 컨테이너는 구성이 포함된 람다를 받는 startKoin 함수를 통해 초기화됩니다. 이 람다 내에서 DI 로직의 주요 구성 요소인 등록이 포함된 모듈이 설명됩니다.
startKoin 함수는 GlobalContext를 통해 애플리케이션 어디에서나 액세스할 수 있는 전역 컨테이너를 생성하지만, 멀티 모듈 프로젝트에서는 격리된 컨테이너를 만들기 위해 KoinApplication을 사용하는 것이 좋습니다. Android에서는 초기화에 AndroidContext가 사용되며, 이는 자동으로 Application 수명 주기에 바인딩됩니다. 모듈은 Module 인스턴스 목록을 받는 modules 매개변수를 통해 등록됩니다.
val networkModule = module {
single {
OkHttpClient()
}
single {
Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
startKoin {
modules(networkModule)
}
각 모듈에는 single(싱글톤) 또는 factory(새 인스턴스)를 통한 정의가 포함됩니다. 정의는 get()을 통해 다른 등록된 종속성을 참조할 수 있으며, 명시적인 유형 지정과 boilerplate 코드 없이 주입 그래프를 형성합니다.
Koin은 컨텍스트에서 유형 추론을 위해 reified 매개변수가 있는 인라인 함수를 적극적으로 사용합니다. 이를 통해 클래스를 명시적으로 지정하지 않고 등록을 작성할 수 있습니다. single { MyService() }는 람다의 반환 값에서 자동으로 유형을 결정합니다.
Dagger와 달리, Koin은 컴파일 타임에 종속성 그래프를 확인하지 않습니다. 모든 오류는 확인되지 않은 종속성에 처음 액세스할 때 런타임에서 감지됩니다. 이는 코드를 크게 단순화하고 빌드 속도를 높이는 절충안이지만, DI 구성의 테스트 커버리지가 필요합니다. 많은 팀이 컴파일 타임 검사가 없음에도 불구하고 개발 속도와 단순성 때문에 Koin을 선택합니다.
Koin 버전 3.5에서는 Koin Annotations 플러그인을 통한 실험적인 컴파일 타임 그래프 검사가 도입되었습니다. 개발자는 @Module 및 @KoinComponent 어노테이션을 추가하고, 플러그인은 빌드 중에 실행되는 검증 코드를 생성합니다. 그러나 Koin의 주요 장점인 코드 생성 없음이 이 모드에서는 사라지므로, 대부분의 팀은 테스트를 통한 런타임 검사와 함께 기존 DSL 방식을 계속 사용합니다.
Koin은 by inject(), get() 및 직접 생성자 전달의 여러 종속성 주입 방법을 제공합니다. 선택은 사용 컨텍스트에 따라 다릅니다.
by inject 델리게이트는 Android ViewModel 및 프래그먼트에서 가장 일반적인 주입 방법입니다. 종속성은 지연 초기화되며, 첫 번째 속성 액세스 시에만 초기화됩니다. 이는 즉시 필요하지 않을 수 있는 리소스 집약적 서비스에 효율적입니다.
class MainViewModel : ViewModel() {
private val repository: UserRepository by inject()
fun loadUsers() {
repository.fetchAll()
}
}
get 함수는 즉시 종속성 인스턴스를 반환합니다. 등록 시 팩토리 람다 내에서 또는 지연 초기화 없이 동기 컨텍스트에서 종속성이 필요할 때 사용됩니다. by inject()와 달리, get()은 지연 로딩을 지원하지 않으며 호출 시점에 컨테이너가 이미 초기화되어 있어야 합니다.
Koin의 스코프는 종속성 수명을 Activity, Fragment 또는 사용자 지정 세션과 같은 특정 구성 요소에 바인딩하는 메커니즘입니다. 이는 Android 애플리케이션의 메모리 관리에 중요한 기능입니다.
모듈 내의 scope 함수는 바인딩된 구성 요소가 유지되는 동안 유지되는 스코프를 생성합니다. 스코프에 등록된 모든 종속성은 스코프가 닫힐 때 제거되어 메모리 누수를 방지합니다.
val userScope = module {
scope<UserSession> {
scoped {
UserRepository(get())
}
scoped {
SessionManager(get())
}
}
}
scoped 함수는 스코프 내에서만 존재하는 종속성을 등록합니다. 스코프가 닫히면 모든 scoped 객체가 가비지 컬렉션 대상이 됩니다.
single은 지연 초기화를 통해 전체 애플리케이션에 대해 단일 인스턴스를 등록합니다. 네트워크 클라이언트, 캐시, 로거와 같은 상태 비저장 서비스에 사용됩니다.
factory는 get()이 호출될 때마다 새 인스턴스를 생성합니다. ViewModel, 리포지토리 및 액세스할 때마다 새 인스턴스가 중요한 상태 저장 객체에 적용됩니다.
Koin을 Android 프로젝트에 통합하는 것은 최소한입니다. build.gradle에 종속성을 추가하고 Application.onCreate에서 startKoin을 호출하기만 하면 됩니다. Koin은 Jetpack Compose, Navigation 및 WorkManager와의 통합 모듈을 제공하여 Hilt의 완전한 대안이 됩니다.
특수 koin-android-compose 라이브러리를 사용하면 koinViewModel() 및 koinInject()를 통해 Composable 함수에 직접 종속성을 주입할 수 있습니다. 이를 통해 각 화면의 매개변수를 통해 컨테이너를 전달할 필요가 없어지며, 자동 수명 주기 바인딩을 통해 ViewModel 코드가 더 깔끔해집니다.
Google I/O 2024에 따르면, Jetpack Compose는 새로운 Android 프로젝트의 기본 프레임워크가 되었습니다. Koin은 추가 구성 없이 Compose에 대한 기본 지원을 제공하며, 코루틴 컨텍스트를 고려하여 koinViewModel()을 통해 스코프를 ViewModel 수명 주기에 자동으로 바인딩합니다.
테스트를 위해 Koin은 koinTest 및 koinTestRule 함수를 제공하여 테스트 모듈로 격리된 테스트 컨테이너를 만들고 테스트 완료 후 자동으로 닫습니다. 이는 테스트 격리를 보장하고 테스트 케이스 간 상태 누출을 방지합니다.
Jetpack Navigation과의 Koin 통합은 koin-androidx-navigation 모듈을 통해 구현됩니다. 각 화면의 ViewModel은 화면 회전 시 상태 보존 및 애플리케이션 중단 후 복구를 위해 SavedStateHandle을 전달하여 by viewModel()을 통해 자동으로 종속성을 받습니다.
Koin을 사용한 ViewModel의 단위 테스트를 위해 koin-test-junit5 또는 koin-test-junit4 라이브러리의 koinTestRule이 사용됩니다. 이 규칙은 각 테스트 전에 테스트 모듈로 격리된 컨테이너를 만들고 완료 후 자동으로 닫아 테스트 케이스 간 상태 누출을 방지합니다. 실제 종속성은 MockK를 통해 모의 객체로 대체됩니다. single
Koin 3.x의 주요 기능 중 하나는 Kotlin으로 서버 애플리케이션을 구축하기 위한 Ktor와 데스크톱 애플리케이션을 위한 Compose Multiplatform을 지원한다는 것입니다. 이로 인해 Koin은 주입 패러다임을 변경하지 않고도 세 가지 Kotlin 플랫폼을 모두 지원하는 유일한 DI 프레임워크입니다. koin-ktor 모듈을 사용하면 Application 블록에서 install(Koin)을 통해 종속성을 등록하고 Android에서와 같이 by inject()를 통해 라우트에 서비스를 주입할 수 있습니다. 이로 인해 Koin은 모바일 클라이언트에서 서버 백엔드에 이르기까지 모든 아키텍처의 Kotlin 프로젝트를 위한 범용 DI 솔루션이 됩니다.
koin-androidx-navigation 모듈을 통한 Jetpack Navigation과의 Koin 통합은 각 화면에 대해 수동으로 ViewModelProvider.Factory를 생성할 필요를 없앱니다. 멀티 모듈 프로젝트의 경우 Koin은 loadKoinModules를 통한 지연 모듈 로딩을 지원하여 각 기능 모듈이 독립적으로 DI 구성을 연결할 수 있습니다.
자주 묻는 질문
Koin은 코드 생성이나 어노테이션 없이 런타임에서 작동하여 빌드 속도를 높이지만 컴파일 타임에 종속성 그래프를 확인하지 않습니다. Hilt는 컴파일 타임에 코드를 생성하고 DI 오류를 더 일찍 발견하지만, 복잡한 설정이 필요하고 빌드 속도가 느려집니다.
네, Koin은 Kotlin Multiplatform을 완벽하게 지원합니다. koin-core 라이브러리는 모든 Kotlin 플랫폼에서 작동하며, koin-android와 koin-compose는 각각 Android와 iOS에 대한 플랫폼별 기능을 추가합니다.
순환 종속성은 런타임에서 StackOverflowError를 발생시킵니다. Koin은 이를 자동으로 감지하지 않습니다. 해결책은 아키텍처 리팩토링입니다. 공통 인터페이스 추출, Listener/Observer 패턴 사용 또는 지연 초기화가 있는 팩토리를 통한 순환 해결입니다.
Android에서는 스코프를 AndroidScope를 통해 Activity 또는 Fragment 수명 주기에 바인딩할 수 있습니다. 구성 요소가 소멸되면 Koin이 자동으로 해당 스코프를 닫습니다. 사용자 지정 스코프(사용자 세션)에서는 scope.close를 호출하여 수동으로 닫습니다.
koin-test 모듈의 koinTest 함수를 사용하세요. 테스트 모듈로 격리된 컨테이너를 만들고 테스트 후 자동으로 닫습니다. 실제 종속성은 Mockito 또는 MockK를 사용하는 모듈을 통해 모의 객체로 대체됩니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.