Kotlin으로 DI를 공부하면서 가장 먼저 접하는 라이브러리는 kotlin DSL로 만들어진 Koin입니다.
Koin은 Dagger에 비해 구성요소가 복잡하지 Koin의 러닝커버는 다른 라이브러리보다 낮습니다.
DSL(Domain Specific Language) : 특정 도메인(산업, 분야등 특정 영역)에 국한해 사용하는 언어를 말합니다.
Module 선언(생성) - "Koin DSL"
Application 단위 Class에서 startKoin()으로 Koin 실행
의존성 주입 - 구성요소 (Activity, Fragment, Class 등)
Module은 객체를 제공하는 명세를 의미 factory, single , scoped 생성 문법 먼저 Module에서 제공할 의존성들의 클래스 구조를 선언해줍니다.
현재 제가 진행중인 프로젝트에서 사용되고 있는 코드들입니다.
val appModule = module {
factory { (homeListCategory: HomeListCategory) ->
HomeListViewModel(homeListCategory, get())
}
viewModel { HomeMarketMenuViewModel(get()) }
single<SuggestRepository> {DefaultSuggestRepository()}
single : 싱글톤으로 생성해서 의존성주입(App 수명주기 동안 단일 인스턴스)
factory : 요청(Inject, get) 시점마다 새로운 인스턴스를 생성(Dagger의 Provider 개념)
객체 생성자 파라미터로 get()을 넣어줍니다.
Application 단위 Class에서 Koin Start를 하기 위해 Application Class 선언
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
appContext = this
startKoin {
androidLogger(Level.ERROR) // AndroidLogger를 Koin logger로 사용합니다.
androidContext(this@MyApplication)
modules(listOf(appModule,viewModelModule,repositoryModule))
}
}
}
class HomeMainFragment
: BaseFragment<FragmentHomeMainBinding>(),
AdapterView.OnItemSelectedListener {
private val resourcesProvider by inject<ResourcesProvider>()
private val viewModel by viewModel<HomeMainViewModel>()
}
single, factory는 by inject() 또는 get()을 사용해 Activity/Fragment에서 의존성을 주입하고
ViewModel은 by viewModel()을 사용해 의존성을 주입합니다.
ViewModel의 데이터를 공유받고 싶다면 sharedViewModel<>
private val activityViewModel by sharedViewModel<MainViewModel>()
Koin은 리플렉션을 이용해 런타임에 오브젝트 그래프를 그려주다 보니(=의존성 주입을 하다보니) 앱 성능이 저하된다는 점이 있습니다. (실제로 회사들의 기술블로그를 본다면 Koin을 사용하다 Dagger-Hilt로 전환하는 곳도 많았습니다.)
그렇기에 큰 규모의 프로젝트에서는 Koin을 사용할 경우 Application이 시작될 때 의존성 그래프가 그려지다 보니 화면이 멈춘 것처럼 될 수 있다. 따라서 큰 규모의 프로젝트에서는 Dagger-Hilt을 이용해 의존성을 주입하는 것이 좋습니다.
Run time
-> Koin : 의존성 주입(영향 있음)
-> Dagger : 영향 없음
Compile Time
-> Koin : 영향 없음
-> Dagger : 의존성 주입