본문 바로가기

코딩/안드로이드

Android Kotlin MVVM패턴으로 간단한 검색 앱 만들기 - 2. Koin을 통한 의존성 주입

의존성 주입.. Dependency injection ... 줄여서... DI....



이걸 왜하느냐!


라고 물으시면..



아주 간단하게,


테스팅을 위해서!

라고 답할 수 있지 않을까요..? 





지금과 같은 MVVM패턴에서


뷰모델을 테스팅한다고 칩시다.


그럼 뷰모델을 테스팅하려면 뷰모델이 의존하고 있는 모델은.. 어떻게 만들어서 뷰모델에게 줘야 뷰모델을 테스팅 할 수 있을까요?



간단한 방법으로는 모델을 내부에서 생성하지 않고 생성자의 인자로 주는 것입니다.



ViewModel{

val model = Model()

}


과 같이 하지 않고


ViewModel(val model){

}


이렇게



그러면 뷰모델을 테스팅모듈에서 생성할 때 테스팅용 모델을 뷰모델의 생성자로 '주입' 해줌으로서 뷰모델을 테스팅하기 쉽게 할 수 있는 것이죠.




근데 이걸 일일이 한다?



꽤.. 불편하고 버그도 많게 될거 같군요..


그래서 이러한 의존성 주입 작업을 편하게 해주는 몇가지 라이브러리가 있습니다.


그중 가장 대표적인것이 Dagger2 !


근데... 대거는.. 너무 어렵습니다!


솔직히 저같은 초보는..


의존성이 적은 MVVM패턴 같은 데서는


대거 배워서 적용한다음 대거를 위해 프로젝트 구조를 싹 다 바꾸는 것보다..


그냥 라이브러리 없이 의존성 주입하는게 더 편합니다...ㅜㅜㅜ




근데, Koin은 너무 편합니다!


학습할 것도 많이 없고, 바꿀것도 많이 없고, 코틀린에 특화되어있고, 뷰모델에 또한 특화되어있기도 하지요.


관련되서는 이 링크를 한번 보시면 좋을듯 합니다.

https://medium.com/harrythegreat/kotlin%EC%97%90%EC%84%9C-dagger2-%EC%93%B0%EA%B8%B0-%ED%9E%98%EB%93%9C%EB%8B%88-%EA%B7%B8%EB%9F%BC-%EB%84%8C-koin%EC%9D%B4%EC%95%BC-e9e42ec1288e



그래서, 저는 코인을 쓰고, 이번 글에서는 코인에 관해 간단하게 설명해보겠습니다.







일단 코인 공식 홈페이지입니다.


https://insert-koin.io/



이건 깃헙 리포지토리 입니다.


https://github.com/InsertKoinIO/koin




사용방법을 말씀드리며 설명을 해보겠습니다.



일단 디펜던시에 추가해줍시다


// koin
implementation "org.koin:koin-androidx-scope:1.0.2"
implementation "org.koin:koin-androidx-viewmodel:1.0.2"

AndroidX용 입니다. AndroidX를 안쓰시면 깃헙 레포지토리 리드미등을 참고해서 다른걸로 바꿔주세요 




먼저 의존성 주입에 사용할 모듈을 짜봅시다.


이 모듈이 뭐냐면, 간단하게 말해서 의존성 주입을 하는 실제 코드, 혹은 의존성 주입을 위한 설계도, 정도로 이해해주시면 됩니다.


var modelPart = module {
factory<DataModel> {
DataModelImpl()
}
}

var viewModelPart = module {
viewModel {
MainViewModel(get())
}
}

var myDiModule = listOf(modelPart, viewModelPart)


끝입니다.


간단하죠!


대거로 짰으면 벌써 클래스 6개는 나왔습니다... 




일단 factory{}와 viewModel{}이 보이네요


팩토리는 말그대로 공장입니다. DataModelImpl()이라는 클래스를 뚝딱뚝딱 만들어 주죠


그러면 이제 다른 클래스에서 해당 부분이 필요하다면 단순히 get()을 해주면 팩토리로 만든 클래스가 쏘옥~하고 들어갑니다.


팩토리외에도 single{}이 있는데, 이건 싱글톤패턴처럼 어플리케이션에서 단 하나만 만듭니다. 저는 보통 Retrofit을 통해 만든 서비스 클래스를 single로 만듭니다.


이렇게 얻은 객채들은 방금 말한것처럼 get()으로 넣는것 이외에도 by inject()를 통해서도 얻을 수 있습니다.



그리고 viewModel{}은 말 그대로 뷰모델을 만듭니다.


액티비티에서 by viewModel()을 통해서 얻어올 수 있죠.




두번째로 Application 클래스를 상속 한뒤 onCreate에 딱 한줄만 넣습니다.


class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin(applicationContext, myDiModule)
}
}

startKoin(context, module)


이러면 이제 의존성이 주입됩니다.



(Application 클래스를 상속한 뒤 메니페스트에서

<application
android:name=".MyApplication"

와 같이 설정해 주는걸 잊지 마세요!)



주입할 때는 이런식으로 코드 내에서나,



val service : BusinessService by inject()



이렇게 생성자나,


class Controller(val service : BusinessService){ 
  
  fun hello() {
     // service is ready to use
     service.sayHello()
  }
} 



이렇게 뷰모델을 주입할 수 있습니다.


  val vm : MyViewModel by viewModel()





추가로 테스팅을 할 때에는 



// Just tag your class with KoinTest to unlick your testing power
class SimpleTest : KoinTest { 
  
  // lazy inject BusinessService into property
  val service : BusinessService by inject()

  @Test
  fun myTest() {
      // You can start your Koin configuration
      startKoin(myModules)

      // or directly get any instance
      val service : BusinessService = get()

      // Don't forget to close it at the end
      stopKoin()
  }
} 




이렇게 해주면 됩니다.






실제로 이전 게시물의 메인 액티비티에서는

override val viewModel: MainViewModel by viewModel()
와 같이 뷰 모델을 주입해주게 구현했습니다.







다음 게시물에서는 RxJava와 Retrofit을 통해 카카오 Api로 검색하는 것을 설명해보겠습니다.