본문 바로가기

[Android][Kotlin] Koin #1 - 기본 사용방법

Koin #1 - 기본 사용방법

 

Koin ?

 

Dagger vs Koin

  • 러닝커브 - Dagger (높음) / Koin (낮음)
  • DI 주입 - Dagger (컴파일 시 주입, 컴파일 오버헤드) / Koin (런타임 시 주입, 상대적으로 낮은 런타임 오버헤드) 

 

Koin DSL ?

DSL (Domain Specific Language)의 약어로, '도메인 특화 언어'를 의미

(특정한 도메인을 적용하는데 특화된 언어 - 위키피디아)

 

DSL 키워드

  • module : koin 모듈 정의 (Module - 제공할 객체의 명세)
  • viewModel : Activity나 Fragment에 각 ViewModel을 주입
  • factory : 의존성 주입(Inject / get) 시점마다 새로운 객체를 매번 생성, Dagger의 Provider 같은 개념
  • single : 해당 객체를 싱글톤으로 생성 (App Lifecycle 전체동안 단일 인스턴스)
  • bind : 생성할 객체를 다른 Type으로 바인딩 (Class, Interface 상속관계 필요)
  • get() : Component 내에서 알맞은 의존성을 주입
  • inject() : get과 같이 알맞은 의존성을 주입 ( by inject() 방식, val에만 가능, var변수에 사용 불가)

 

Koin 동작 방식

  1. Module 선언(생성) - "Koin DSL"
  2. Application 단위 Class에서 startKoin()으로 Koin 실행
  3. 의존성 주입 - 구성요소 (Activity, Fragment, Class 등)

 

Gradle 설정

먼저 gradle Project 수준을 설정을 한 뒤 app 수준의 설정이 필요  

build.gradle(Project)
buildscript {
    ext.kotlin_version = '1.3.72'
    ext.koin_version = '2.1.5'	// Koin 최신버전
    repositories {
        google()
        jcenter()	// Koin 사용 시 jecenter() 필수
    }
    ...
}

allprojects {
    repositories {
        google()
        jcenter()	// Koin 사용 시 jecenter() 필수
    }
}

buildscript 부분에 ext.koin_version = '최신버전'을 입력합니다 (dependencies에서 사용할 값)

2020.05.25 기준 최신버전 '2.1.5' [ 최신버전 확인 ] 

기본적으로 repositories에 jcenter()가 추가되어 있지만, 없을 경우 추가필요

다음은 app수준의 gradle 설정 (dependencies 추가)

build.gradle(app)
dependencies {
    ...

    // Koin for Kotlin (핵심 옵션)
    implementation "org.koin:koin-core:$koin_version"
    // Koin extended & experimental features (핵심 옵션)
    implementation "org.koin:koin-core-ext:$koin_version"

    // Koin for Unit tests
    testImplementation "org.koin:koin-test:$koin_version"

    // Koin for Android
    implementation "org.koin:koin-android:$koin_version"
    // Koin Android Scope features
    implementation "org.koin:koin-android-scope:$koin_version"
    // Koin Android ViewModel features
    implementation "org.koin:koin-android-viewmodel:$koin_version"
    // Koin Android Experimental features
    implementation "org.koin:koin-android-ext:$koin_version"

    // Koin AndroidX Scope features
    implementation "org.koin:koin-androidx-scope:$koin_version"
    // Koin AndroidX ViewModel features
    implementation "org.koin:koin-androidx-viewmodel:$koin_version"
    // Koin AndroidX Fragment features
    implementation "org.koin:koin-androidx-fragment:$koin_version"
    // Koin AndroidX Experimental features
    implementation "org.koin:koin-androidx-ext:$koin_version"
}

koin-core koin-core-ext 는 핵심옵션으로 필수적으로 추가필요, 나머지는 필요에 따라 추가설정

 

Module 생성

Module은 객체를 제공하는 명세를 의미

factory, single, scoped 생성 문법

먼저 Module에서 제공할 의존성(객체)들의 클래스 구조를 선언

model.kt
class AA {
    fun name() : String = "AA"
}

class BB(aa: AA) {
    fun name() : String = "BB"
}

AA와 BB 클래스를 선언

BB클래스는 생성자 파라미터로 AA 인스턴스를 받습니다

 

myModule.kt
val myModule = module {
    single { AA() }		// 1️⃣

    factory { BB(get()) }	// 2️⃣
}

1️⃣ single
    : 싱글톤으로 생성해서 의존성주입 (App 수명주기 동안 단일 인스턴스)

2️⃣ factory
    : 요청(Inject, get) 시점마다 새로운 인스턴스를 생성(Dagger의 Provider 개념)
      BB 객체 생성자 파라미터로 get() 사용, (위에서 선언한 싱글톤의 AA를 자동으로 의존성주입)

 

App 레벨에서 Koin Start (Koin 초기화)

Application 단위 Class에서 Koin Start를 하기 위해 Application Class 선언

MyApp.class
class MyApp : Application() {

    override fun onCreate() {
        super.onCreate()

        // Koin Start
        startKoin{
            androidContext(this@MyApp)
            modules(myModule)  // 사용할 Module 등록
        }

        // 복수개(여러개) 모듈 등록 시
        // modules(a_Module, b_Module, c_Module)
    }
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// AndroidManifest에 Application class 등록
<application
    android:name=".MyApp"
    ... >

Application을 선언하면 사용을 위해 메니페스트의 Application속성의 name을 등록해야 사용가능합니다

 

의존성 주입 'DI'

single, factory는 by inject() 또는 get() 사용

viewModel은 by viewModel() 사용

MainActivity.kt
class MainActivity : AppCompatActivity() {

    // inject() 의존성 주입 - Lazy 방식
    val bb_inject1 : BB by inject()	// inject Type 유형 1 - Type by inject()
    val bb_inject2 by inject<BB>()	// inject Type 유형 2 - by inject<Type>()

    // get() 의존성 주입 - 바로 주입 방식
    var bb_get1 : BB = get()		// get Tpye 유형 1 - Type = get()
    var bb_get2 = get<BB>()		// get Type 유형 2 - get<Type>()

    override fun onCreate(savedInstanceState: Bundle?) {
          ...

          println("bb_inject1 name is ${bb_inject1.name()}")
          println("bb_inject2 name is ${bb_inject2.name()}")

          println("bb_get1 name is ${bb_get1.name()}")
          println("bb_get2 name is ${bb_get2.name()}")
    }
}

single, factory의 의존성 주입 메서드로 by inject() / get() 사용방법 입니다.

inject와 get 방식 둘 다 Type 선언 방식은 2가지로 구분

    1) var 변수명: Type = get() 형식 - Koin이 해당 변수의 Type을 보고 알맞은 의존성을 주입

    2) var 변수명 = get<Type>() 형식 - Koin이 get()함수에 선언된 Type에 맞는 의존성을 주입

Inject와 get 방식의 차이

    inject - Lazy 방식의 주입, 해당 객체가 사용되는 시점에 의존성 주입

    get - 바로 주입, 해당 코드 실행시간에 바로 객체를 주입

 

확실히 Dagger2 사용할 때 Component / SubComponent / Module / DaggerInjection 과정을 생각하면 훨씬 간단하고 간편한게 느껴집니다

 

의존성주입 (inject, get)의 여러가지 방법과 Scope 등 다음에 이어서 작성하겠습니다

 

 

참고