
이번 게시글에서는 Koin에 대해 알아보고자 합니다.
Koin이란?
Koin은 Kotlin에 특화된 경량화된 의존성 주입 프레임워크입니다.
koin은 다음과 같은 2가지의 의존성 주입 방식이 있습니다.
- Kotlin DSL
- Annotation
2가지 방식의 기능 차이는 거의 존재하지 않기에 원하는 방식을 사용하여 의존성을 주입할 수 있습니다.
런타임 의존성 주입
koin은 기본적으로 런타임에 의존성을 주입해 줍니다.
런타임의 장점은 유연하다는 것입니다. 다른 의존성 주입 라이브러리와 달리 상황에 따라 동적으로 의존성을 주입할 수 있습니다.
// 동적 모듈 로딩 (Hilt 불가)
if (featureEnabled) {
loadKoinModules(premiumFeatureModule)
}
// 기능 비활성화 시
unloadKoinModules(premiumFeatureModule)
하지만, 런타임에 의존성을 주입하면 애플리케이션의 실행 중에 의존성 관련 오류가 발생할 수 있기에, koin은 `Koin Compiler Plugin`을 통해서 의존성 그래프 검증을 수행합니다.
Koin Compiler Plugin은 Kotlin 네이티브 컴파일러 플러그인(K2)로 , 다음과 같은 기능을 제공합니다.
- 생성자 의존성을 자동으로 감지합니다.
- 컴파일 타입 분석을 제공합니다.
- DSL과 Annotation 방식 모두에서 동작합니다.
- 별도의 파일을 만들지 않습니다.
- KSP를 사용하지 않습니다.
Koin은 이러한 방식을 통해, 런타임 의존성 주입이 가지는 유연성과 컴파일 타임 의존성 검증이 제공하는 안정성을 모두 확보할 수 있습니다.
2가지 의존성 주입 스타일
Koin은 의존성 정의를 위한 두 가지 스타일을 지원하며, 기능 차이는 없습니다.
Kotlin DSL 스타일
val appModule = module {
single<Database>()
single<ApiClient>()
single<UserRepository>()
viewModel<UserViewModel>()
}
Annotation 스타일
@Singleton
class Database
@Singleton
class ApiClient
@Singleton
class UserRepository(
private val database: Database,
private val apiClient: ApiClient
)
@KoinViewModel
class UserViewModel(
private val repository: UserRepository
) : ViewModel()
자세한 사용 방법에 대해서는 나중에 알아보겠습니다.
Koin의 핵심 기능
이제부터는 Koin의 핵심 기능들에 대해서 알아보겠습니다.
startKoin
Koin에는 컨테이너라는 개념이 존재합니다. 컨테이너는 의존성을 보관하고, 필요할 때 꺼내 주입해 주는 저장소입니다.
`startKoin`은 Koin 컨테이너를 초기화하고 애플리케이션에 맞게 설정할 수 있는 메인 진입점이 되는 메서드입니다. 이 함수는 컨테이너를 GlobalContext에 등록하여, 애플리케이션 전역에서 접근할 수 있도록 만듭니다.
startKoin {
modules(appModule)
}
modules
Koin `module`은 의존성 주입 구성을 체계적으로 정리하기 위한 기본 구성 요소입니다.
`module`은 "의존성들을 모아둔 묶음 파일"이라고 보면 됩니다. `module`은 서로 관련된 의존성들을 하나로 캡슐화하여, 기능이나 계층 단위로 의존성을 구성할 수 있도록 합니다.
val appModule = module {
single<Database>()
single<UserRepository>()
viewModel<UserViewModel>()
}
이렇게 정의한 모듈은 `startKoin` 에서 등록할 수 있습니다. 한번에 여러 모듈을 등록하는 것 또한 가능합니다.
startKoin {
modules(dataModule, viewModelModule)
}
includes
`includes`는 모듈을 구성할 때 권장되는 방식입니다. 다음과 같은 장점을 제공합니다.
- 모듈 계층 구조: 부모-자식의 관계로 모듈을 구조화할 수 있음
- 로딩 최적화: Koin이 포함된 모듈을 중복 제거해서 불필요한 등록을 막아줌
- 깔끔한 시작 구성: 긴 모듈 목록 대신에 하나의 루트 모듈만 등록
- 캡슐화: 내부 모듈을 공새 API 모듈 뒤에 숨길 수 있음
Koin은 모듈을 구성할 때, 모든 모듈을 나열하는 대신 `includes`를 사용해서 모듈 계층을 구성하는 것을 권장합니다.
val networkModule = module {
single<ApiClient>()
}
val storageModule = module {
single<Database>()
}
// 부모 모듈이 자식 모듈을 포함
val dataModule = module {
includes(networkModule, storageModule)
single<UserRepository>()
}
startKoin {
modules(dataModule)
}
Definitions
`Definitions`은 Koin이 의존성을 생성하고 관리하는 방식을 정의합니다.
`Definitions`의 타입은 다음과 같이 4가지가 존재합니다.
Single
앱 전반에서 재사용되는 하나의 인스턴스를 생성합니다.
// DSL
single<DatabaseHelper>()
// Annotation
@Singleton
class DatabaseHelper
Factory
요청할 떄마다 새로운 인스턴스를 생성합니다.
// DSL
factory<UserPresenter>()
// Annotation
@Factory
class UserPresenter(private val repository: UserRepository)
Scoped
스코프마다 하나의 인스턴스를 생성합니다.
// DSL
scope<MyActivity> {
scoped<ActivityPresenter>()
}
// Annotation
@Scoped(MyActivityScope::class)
class ActivityPresenter
ViewModel
적절한 생명주기를 따르는 Android ViewModel을 생성합니다.
// DSL
viewModel<UserViewModel>()
// Annotation
@KoinViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel()
Interface Binding
구현체가 아니라 인터페이스 타입으로 주입하기 위해서는 다음과 같이 바인딩을 정의합니다.
single<UserRepositoryImpl>() bind UserRepository::class
Annotation을 활용하면 자동으로 바인딩을 수행해 주기에 더 편리합니다.
@Singleton
class UserRepositoryImpl(
private val database: Database
) : UserRepository
만약 명시적으로 바인딩을 정의하고 싶다면 다음과 같이 할 수 있습니다.
@Singleton
class UserRepositoryImpl(
private val database: Database
) : UserRepository
1편 마무리
이번 게시글에서는 Koin에 대해서 기본적으로 알아보고, 몇 가지 Koin의 핵심 기능들에 대해 알아보았습니다.
분량이 너무 많아 다음 게시글에서 이어서 Koin의 나머지 핵심 기능들에 대해서 알아보고자 합니다.
Reference
What is Koin? | Koin
The Pragmatic Kotlin Dependency Injection Framework - Simple AND Powerful
insert-koin.io
Definitions | Koin
Definitions declare how Koin creates and manages your dependencies. This guide covers all definition types using both DSL and Annotations.
insert-koin.io
'Android > Kotlin' 카테고리의 다른 글
| Koin 톺아보기 2편 (0) | 2026.02.12 |
|---|---|
| 코루틴은 왜 경량 스레드라 불리는가? (1) | 2025.12.19 |
| 안드로이드의 MessageQueue 이해하기 (0) | 2025.04.08 |
| [Flow 연산자] 생산자 총정리 (0) | 2025.03.31 |
| 코루틴의 구조적 동시성(Structured Concurrency) (0) | 2025.03.25 |