Android

[Android] 안드로이드 - Koin (코틀린 개발자를 위한 경량화된 DI 프레임워크) 사용법

NukeOlaf 2020. 6. 6. 06:17

1. Koin 이란?

Koin 이란, 순수 kotlin 으로 작성된 Dependency Injection 프레임워크 (이하 DI 프레임워크)이다.

DIInverse Of Control (제어 역전) 을 통해 객체끼리의 의존 관계를 분리시키는 디자인 패턴을 말한다.

즉, 쉽게 말해서 객체 A 내부에서 객체 B 를 어떤 방식으로든 사용하는 경우 객체 B 에 대해 객체 A 가 의존하고 있다고 볼수 있으며, 객체 A 에서 객체 B 를 초기화 하지 않고 사용할 수 있도록 외부에서 객체 B 를 초기화하여 객체 A 에 주입해주는 것을 DI 라고 생각할 수 있다.

DI 관련 내용 정리 포스팅 : https://salix97.tistory.com/264

안드로이드에서 대표적으로 많이 사용하는 DI 프레임워크로 Dagger 가 있지만, Koin 에 비해 상대적으로 코드를 더 많이 생성하며, 러닝커브가 높다.

 

2. Koin 사용법 (with MVVM)

MVVM 패턴에서의 Model 레이어에 Koin 을 적용하는 과정을 예로 들어 작성했다.

(1) Koin 의 버전을 확인하고 Module: app 수준의 build.gradle 에 필요한 dependency 를 추가해준다.

2020.6.6 기준 current version : 2.1.5

// Koin AndroidX Scope feature
implementation "org.koin:koin-androidx-scope:2.1.5"

// Koin AndroidX ViewModel feature
implementation "org.koin:koin-androidx-viewmodel:2.1.5"

// Koin AndroidX Fragment Factory (unstable version)
implementation "org.koin:koin-androidx-fragment:2.1.5"

 

(2) DI 를 사용할 DataSource 와 Repository, ViewModle 은 다음과 같이 작성했다. 

interface LocalDataSource {}
class LocalDataSourceImpl(context: Context) : LocalDataSource {}

interface RemoteDataSource {}
class RemoteDataSourceImpl(api: RetrofitApi): RemoteDataSource {}

interface Repository {}
class RepositoryImpl(
    private val localDataSource: LocalDataSource,
    private val RemoteDataSource: RemoteDataSource
) {}
class MyViewModel(
    private val Repository: Repository
) : ViewModel() {}

 

(3) module 함수를 이용하여 다음과 같이 모듈을 생성했다. 

// Model 레이어에 대한 모듈
val appModule = module {
    
    single<LocalDataSource> { LocalDataSourceImpl(androidContext) }
    single<RemoteDataSource> { RemoteDataSourceImpl(get()) }
    single<Repository> { RepositoryImpl(get(), get()) }
    
}
// 레트로핏 객체에 대한 모듈
val retrofitModule = module {
    
    single {
        OkHttpClient.Builder()
            .addInterceptor(get<Interceptor>())
            .addInterceptor(get<HttpLoggingInterceptor>())
            .build()
    }

    single {
        Interceptor { chain: Interceptor.Chain ->
            val original = chain.request()
            chain.proceed(original.newBuilder().apply {
                addHeader("Authorizaion_Header", "access_token")             
            }.build())
        }
    }

    single {
        HttpLoggingInterceptor().apply {
            level = if (BuildConfig.DEBUG) {
                HttpLoggingInterceptor.Level.BODY
            } else {
                HttpLoggingInterceptor.Level.NONE
            }
        }
    }

    single {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(get())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    single {
        get<Retrofit>().create(
            RetrofitInterface::class.java
        )
    }
    
}
// ViewModel 에 대한 모듈
val viewModelModule = module {
    viewModel { MyViewModel(get()) }
}

 

* Koin 에서 사용할 수 있는 DSL (Domain Specific Langauge) 은 다음과 같다
A quick recap of the Koin DSL keywords:

  • module { } - create a Koin Module or a submodule (inside a module)
  • factory { } - inject 할 때마다 인스턴스 생성
  • single { } - 싱글톤으로 생성
  • get() - resolve a component dependency
  • named() - define a qualifier with type, enum or string
  • bind - additional Kotlin type binding for given bean definition
  • binds - list of additional Kotlin types binding for given bean definition
  • getProperty() - resolve a Koin property

 

(4) 이제 application class 에서 startKoin() 함수를 호출하여 위에서 선언한 module 들을 시작시킨다.

참고로, Application 을 Manifest 에 등록해야 동작한다.

class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidContext(this@MovieApp)
            modules(
                appModule,
                retrofitModule,
                viewModelModule
            )
        }
    }
}
<application
    android:name=".MyApplication" // Manifest 에 Application 등록
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

 

(5) Activity Class 에서 "by inject()" delegate injector 를 사용하여 필요한 객체를 주입한다.

by inject() 함수는 Android 컴포넌트의 runtime 에서만 Koin 인스턴스를 사용할 수 있게 한다 (Activity, Fragment, Service...)

class MainActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by inject()

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

 

참고 링크 >>>

koin 공식 문서 : https://start.insert-koin.io/#/

https://blog.yudiz.com/what-is-koin-kotlin-dependency-injection/

https://jungwoon.github.io/android/2019/08/21/Koin/

https://medium.com/@velmm/koin-dependency-injection-framework-for-android-aa34c0737956