Koin Library 사용하기(2편-심화편 MVVM에 적용하기)
해당 문서는 Koin공식 문서를 기반으로 만들어졌습니다.
공식문서: https://insert-koin.io/
Koin Library 사용하기(1편-기본편)에 조금 심화편인 MVVM에 Koin적용하기 편입니다. 사실 이번편을 학습하기 위해서는 선행 학습이 되어있어야 하는것들이 있어 링크로 남겨놓겠습니다.
“Android개발자가 본 DI 란??(Dependency Injection)”은 필수적으로 선행되었으면 하는 바람이 있습니다.
“Koin Library 사용하기(1편-세팅편)”은 본인의 학습정도에 따라서 후행학습하더라도 큰 문제가 없을것으로 판단됩니다.
사용된 DSL은 아래와 같으며 복습차원에서 한번 더 기술하겠습니다.
startKoin{}
- 어플리케이션을 생성하고 괄호안에 있는 도메인들을 등록하고 Koin GlobalContext안에 등록합니다.modules{}
- 사용될 모듈을 정의합니다.single{}
- 싱글톤 패턴으로 인스턴스를 생성합니다.(get Instance)factory{}
- 새로운 인스턴스를 생성합니다.(new Class)get{}
- 구성요소들 간의 종속성을 자동으로 매핑시켜줍니다.viewmodel{}
- MVVM의 viewmodel전용으로 이용됩니다.androidLogger()
- 안드로이드 전용 로그를 작성합니다.
Gradle
gradle세팅에서 중요한 내용은 minSdkVersion입니다. 공식 문서에는 나와있지 않지만, 제공되는 샘플로 미루어보아 21버전 이상을 설정해주시는게 좋을 것 같습니다.
최신 koin-android 를 추가하고, koin으로 의존성 주입을 할 수 있는 viewmodel 도메인을 사용하기 위해 koin-viewmodel을 추가해 줍니다.
또한 MVVM에 꼭 필요한 lifecycle라이브러리를 추가해 줍니다.
databinding이용을 위해 databinding도 enable 시켜줍니다.
Viewmodel, LiveData, Databinding 같은경우 별도의 포스팅에서 설명하고 있습니다. 같이 봐보시면 좋을 것 같습니다.
Package 구성하기
패키지 구성은 아래와 같습니다. 실제 프로그램에서는 CarRepository에서 데이터베이스(Room) 또는 서버통신(Retrofit)을 통해서 데이터를 가져와야하지만, 내용이 복잡할것 같아 model패키지 안에 재료들을 만들어 놓았습니다.
Room에 대한 사용법에 대해서는 링크 남겨놓도록 하겠습니다.
권장 아키택쳐를 그림으로 표현하면 아래와 같습니다.(해당 패키지는 사실상 Repository까지만 구현하였습니다.)
Car 클래스 구성요소 추가하기
저번 포스팅에서는 Engine만을 매개변수로 받았지만 Car에 대한 다른 여러 매개변수도 추가하였습니다.(보닛, 핸들, 타이어)
여러 매개변수를 받을때 상용구 코드가 많이 생성될 수 있지만, 이를 Koin을 이용하여 제거할 수 있습니다.
각 클래스는 가독성을 높이기 위해서 별다른 내용을 넣지 않았습니다.
테스트 더블이 가능한 Repository만들기
여기서 특이한점은 CarRepository를 상속받아 구현하는 구현채인 CarRepositoryImpl을 만들었다는 점입니다. 이는 테스트 더블을 위하여 구현하였습니다. 전기차에 대한 Repository를 만들고 싶다면 CarRepository를 상속받아 구현하는 구현채인 ElectronicCarRepositoryImpl을 사용하면 되며, 여러 다른 테스트를 할때도 StubCarRepositoryImpl등으로 구현하여 테스트가 가능해집니다.
테스트더블에 관한 내용은 링크 남겨놓겠습니다.
Viewmodel 만들기
Viewmodel은 별다른 기능없이 Car를 시작하여(시동을 거는 느낌으로) 그 결과를 화면에 ObservableField를 통하여 세팅하는 클래스로 구성하였습니다.
Viewmodel에 관한 내용은 링크 남겨놓겠습니다.
Application에서 의존성 주입 준비하기(가장 중요)
이부분은 설명을 조금 길게 해볼까 합니다. 준비 되셨나요? 심호흡 한번 하고 전체 코드를 보신 후 같이 상세 코드로 넘어가보도록 하겠습니다.
먼저 아래의 코드를 보면 localDataModule을 선언하고 그 안에 facytory를 2개 선언하였습니다.
첫번째 factory에는 Engine(), Bonnet(), Handler(), Tier()어떤것도 인자값으로 받지 않고, 재료가 되는 클래스들을 생성하였습니다.
두번째 factory에는 Car(get()…)을 통하여 Car에 필요한 요소들을 넣어주었으며 이때 get()은 Car에 필요한 인자값들이 선언(factory, single등의 방법으로)이 되어있는지 자동으로 탐색하게 됩니다.
아래의 코드만 설정해 놓으면 사용시 언제 어떻게 어디서 인자값을 전달받았는지 알 수 없지만(제어 역전) 우리는 Car에서 Engine, Bonnet등의 값을 사용할 수 있습니다.
그 다음 repoModule은 repository전용으로 구성하였고 repository는 여러 군대에서 지속적으로 사용하므로 single(SingleTone)으로 구성하였습니다.
CarRepositoryImpl(get())을 통하여 CarRepositoryImpl에 필요한 인자값(Car)을 자동으로 탐색하여 매핑시켜줍니다.
아래의 코드만 설정해 놓으면 우리는 CarRepositoryImpl에서 Car을 사용할 때 언제 어떻게 어디서 초기화되고 인자를 전달받았는지 모르지만(제어 역전) Car을 사용할 수 있습니다.
그 다음 viewModelModule은 viewmodel전용 module로 구성하였고 내부에 viewmodel전용 도메인인 viewmodel{}을 선언하였습니다.
CarViewModel은 CarViewModel(get())을통해 필요한 인자값(CarRepositoryImpl)을 자동으로 받을 수 있습니다. CarViewModel은 테스트더블이 가능하도록 하기 위하여 CarRepository의 구현체를 인자로 받습니다.
제어역전이 되었다라는걸 다시한번 작성하면 눈이 아플수도 있으니 생략하겠습니다. :)
MainActivity에서 어떻게 활용되는지 살펴보고 마무리하겠습니다.
MainActivity에서 의존성 주입하기
by inject()를 통해 CarViewModel에 의존성 주입하였으며 연쇄작용으로 Repository, Model수준의 의존성도 Application에서 설정한 대로 자동으로 주입되었습니다.
우리는 ClassName(get())을 통하여 어떻게 의존성 주입을 해줬으면 좋겠어 라는 설정만 했을 뿐이지 실제로 선언을 한다거나 하지는 않았습니다.
하지만 carVM.startCar()를 통하여 Car의 기능을 모두 사용할 수 있습니다.
또한 상용구 코드들이 제거되어 MainActivity가 매우 깔끔해보입니다.
이로써 안드로이드 MVVM에서 강조하는 관심사 분리가 되어있고, 테스트가 용이한 간단한 MVVM아키택쳐가 완성되었습니다.
마무리
우리가 DI 라이브러리를 쓰는 이유는 제어역전을 통하여 실수를 줄이고, 테스트더블을 통하여 테스트를 다양하게 정밀하게 용이하게 하기 위함입니다. 하지만 DI 라이브러리를 잘못 구사한다면 실수가 늘어나며, 테스트 또한 굉장히 불편해집니다. 이에 기본적인 개념학습이 필요합니다. 포기하지 마시고 저처럼 여러 선배 개발자분들의 도움을 받아 같이 학습해보면 좋을 것 같습니다.
같이보면 좋은 글입니다.
전체 소스코드는 깃허브에 올려두도록 하겠습니다.
TAG: Koin Library 사용하기(2편-심화편 MVVM에 적용하기)
이상 안드로이드 개발자 이종현이었습니다.
재밌게 읽으셨다면 👏🏻눌러 주시는 것도 잊지말아주세요~ 저에게 큰 힘이됩니다. :)
읽어주셔서 감사합니다.