본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[Koin] Koin 에 대해 알아보자 (tutorial)

by 돼지왕왕돼지 2020. 8. 6.
반응형


kotlin koin tutorial


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 을 사용하면 된다.



-
이정도만 알아도 사용하는 데 무리가 없는 Koin 이다.
Dagger 의 learning curve 를 생각해 봤을 때 Koin 은 이 정도로 쉽다.

(물론 Dagger, Koin 모두 각각의 장단이 있다는 점은 꼭 기억해야 한다.)



-

끝!!

반응형

댓글0