https://www.raywenderlich.com/9457-dependency-injection-with-koin
Using Kotlin to Simplify DI
-
Koin 을 써야 하는 이유는 무엇일까?
Koin 은 더 꽉 들어맞고, 더 직관적으로 쓸 수 있다.
Dagger2 를 예로 들어보면, Dagger2 는 먼저 module, component, 그리고 inject annotation 들의 개념에 익숙해져야 한다.
learning curve 는 매우 비싸다. (배우기가 어렵다는 이야기)
이 외에도 scope, subcomponent 들에 대한 추가 학습도 필요하다.
반대로, Koin 은 단순히 module 만 정의하면 된다.
Koin Basics
Koin 을 사용하기 위해서는 3가지 단계만 밟으면 된다.
1. module 정의 : inject 될 대상을 정의한다.
val applicationModule = module{ single { AppRepository } }
2. Koin 시작 : startKoin 함수 한번으로 DI process 를 시작하며 사용하는 module 을 지정하면 된다.
class BaseApplication : Application(){ override fun onCreate(){ super.onCreate() startKoin(this, listOf(applicationModule)) } }
3. Injection : lazy injection 을 아주 쉽게 할 수 있다.
class FeatureActivity : AppCompatActivity(){ private val appRepository:AppRepository by inject() }
-
Koin 의 한계 중 하나는, Activity 에서만 dependency inject 를 할 수 있다는 것이다. (돼왕 : Dagger 도 그런 한계가 있지..)
다른 class type 에서 inject 를 하려면 관련된 constructor 를 반드시 제공해야 한다.
이를 해결하기 위해서 Koin 에서는 KoinComponent 를 따르도록 하고 있다. 그러면 non-Activity 에서도 injection 을 할 수 있다.
이에 대한 예는 나중에 다룬다.
Getting Started
* Koin 을 project 에 넣자.
buildscript 의 ext block 에 koin_version 명시
koin_version = ‘2.1.5'
dependency 넣기
implementation “org.koin:koin-android:$koin_version”
* Module 을 정의하자
-
@ Module.kt
val applicationModule = module(override = true){ factory<SplashContract.Presenter>{ (view:SplashContract.View) -> SplashPresenter(view) } factory<MainContract.Presenter>{ (view:MainContract.View) -> MainPresenter(view) } factory<FeatureContract.Presenter<Student>>{ (view:FeatureContract.View<Student>) -> FeaturePresenter(view) } single<FeatureContract.Model<Student>>{ AppRepository } single<SharedPreferences>{ androidContext().getSharedPreferences(“SharedPreferences”, Context.MODE_PRIVATE) } single{ Room.databaseBuilder(androidContext(), AppDatabase::class.java, “app-database”).build() } }
factory 는 요청될 때마다 새로운 instance 를 제공해준다.
single 은 app 단위의 singleton 을 지원해준다.
* Koin 을 시작하자.
-
class BaseApplication : Application(){ override fun onCreate(){ super.onCreate() startKoin(this, listOf(applicationModule)) } }
* Inject 하자.
-
private val splashPresenter: SplashContract.Presenter by inject { parametersOf(this) }
parametersOf 는 Koin lib 에서 제공하는 함수로, constructor 의 argument 를 정의한다.
-
class FeaturePresenter(private var view:FeatureContract.View<Student>?) : FeatureContract.Presenter<Student>, KoinComponent{ private val repository: FeatureContract.Model<Student> by inject() }
singleton 이나 parameter 가 필요 없는 경우는 parametersOf 필요 없이 단순 inject call 만 하면 된다.
Activity 이외의 장소에서 inject 를 하려면 KoinComponent 를 반드시 implement 로 추가해주어야 한다.
Koin Testing
-
class FeaturePresetnerTest : KoinTest{ private val view: FeatureContract.View<Student> = mock() private val repository: FeatureContract.Model<Student> by inject() private val presenter: FeatureContract.Presenter<Student> by inject{ parametersOf(view) } @Before fun before(){ startKoin(listOf(applicationModule)) declareMock<FeatureContract.Model<Student>>() } @After fun after(){ stopKoin() } @Test fun `check that onSave2DbClick invokes a repository callback`(){ val studentList = listOf( Student(0, “Pablo”, true, 8), Student(1, “Irene”, false, 10) ) val dummyCallback = argumentCaptor<(String)->Unit>() presenter.onSave2DbClick(studentList) Mockito.verify(repository).add2Db(data = eq(studentList), callback=dummyCallback.capture()) } }
test class 는 반드시 KoinTest interface 를 구현해야 한다.
test 가 되지 않는 모든 object 는 mock 되어야 한다. Koin 에서는 declareMock 을 사용하면 된다.
(물론 Dagger, Koin 모두 각각의 장단이 있다는 점은 꼭 기억해야 한다.)
-
끝!!
'프로그래밍 놀이터 > Kotlin, Coroutine' 카테고리의 다른 글
[kotlin] Sealed class 를 이용하여 error 를 다루는 방법 (1) | 2020.08.08 |
---|---|
[Kotlin] Coroutine 에 대한 이해 : 기본 용어 및 사용 (0) | 2020.08.07 |
[kotlin] crossinline 에 대해 알아보자. (0) | 2020.08.05 |
[coroutine] Shared mutable state and concurrency (0) | 2020.03.13 |
[coroutine] Channels (5) | 2020.03.12 |
댓글