📱 안드로이드 Android ~ Kotlin

[Android/Kotlin] Koin (kotlin 으로 작성된 경량화된 Dependency Injection 프레임워크)

핑크빛연어 2021. 9. 28. 23:53

 

DI (Dependency Injection) 패턴

 - 컴포넌트간의 의존 관계를 소스코드 내부가 아닌 외부 설정 파일등을 통해 정의되게하는 디자인 패턴중 하나
 - 객체를 직접 생성하지 않고 외부에서 주입한 객체를 사용하는 방식
 - 인스턴스 간 디커플링을 만들어줌 => 유닛테스트 용이성 증대
    ( 객체A 내부에서 객체B 를 사용하는 경우 객체B에 대해 객체A가 의존하고 있다고 볼 수 있으며,
      객체A에서 객체B를 초기화하지 않고 사용할 수 있도록 외부에서 객체B를 초기화하여 객체A에 주입해주는 것 )
 Ex) Hilt, Dagger

 

Service Locator 패턴

 - 중앙 등록자 ‘Service Locator’를 통해 요청이 들어왔을 때 특정 인스턴스 반환

 - apk 크기, 빌드 속도, 메서드 수 등 복잡한 제약이 있는 경우 사용하기 편함

 Ex) Koin(경량화 된 DI라고 소개하지만, 내부 동작은 Service Locator로 봐도 무방)

 

🚨 Dependency Injection vs Service Locator

  Dependency Injection Service Locator
종속성 일부 핵심 클래스에 종속성을 주입 모든 클래스가 서비스 로케이터에 종속
호출방법 처음 한번만 호출 (명시적인 호출이 없음) 인젝터를 직접 호출 (명시적인 호출)
의존관계 의존 관계 파악이 쉬움 의존 관계 파악이 어려움

 

 

코인 Koin

순수 kotlin 으로 작성된 경량화된 Dependency Injection 프레임워크로, 내부 동작은 Service Locator 형태를 띈다.

https://insert-koin.io

 

Koin - The Kotlin Injection Framework | Koin

Description will go into a meta tag in

insert-koin.io

 

🚨 Koin 장단점

* 장점
 - 러닝커브가 낮아 쉽고 빠르게 DI를 적용할 수 있다.
 - Kotlin 개발 환경에 도입하기 쉽다.
 - 별도의 어노테이션을 사용하지 않기 때문에 컴파일 시간이 단축된다.
 - ViewModel 주입을 쉽게 할 수 있는 별도의 라이브러리를 제공한다.

* 단점
 - 런타임시 주입이 필요한 컴포넌트가 생성이 되어있지 않는
 파라미터가 있는경우 크래시가 발생할 수 있다.
 - 컴파일 타임에 주입대상을 선정하는 DI에 비해 런타임에 서비스 로케이팅을
 통해 인스턴스를 동적으로 주입해주기 떄문에 런타임 퍼포먼스가 떨어질 수 있다.

 

🚨 Koin 사용 방법

https://insert-koin.io
https://insert-koin.io

 

module{ ... } : 키워드로 주입받고자 하는 객체의 집합
single{ ... } : 앱이 실행되는 동안 계속 유지되는 싱글톤 객체를 생성, 싱글톤scope 로 해당 객체를 만들어서 사용
factory{ ... } : 요청할 때마다 매번 새로운 객체를 생성
get() : 컴포넌트 내에서 알맞은 의존성을 주입

 

 

build.gradle(:app)

하단 dependencies {} 안에 koin 에 대한 라이브러리를 사용하기 위해 추가하여 사용한다.

plugins { ... }
android { ... }

dependencies {

	...

    //koin
    implementation "org.koin:koin-android:2.1.5"
    implementation "org.koin:koin-android-scope:2.1.5"
    implementation "org.koin:koin-android-viewmodel:2.1.5"
    implementation "org.koin:koin-android-ext:2.1.5"

	...

}

 

 

appModule.kt

module{ ... } 키워드를 이용해서 주입받고자 하는 객체의 집합을 모듈로 선언하고 변수에 저장한다.

single{ ... } 키워드를 사용해서 앱이 실행되는 동안 계속 유지되는 객체를 생성한다. repository, retrofit, db 등을 사용할 때 쓴다.

factory{ ... } 키워드를 사용해서 요청할 때마다 매번 새로운 객체를 생성한다. useCase, viewModel 등을 사용할 때 쓴다.

get() 호출 시 자동으로 알맞은 타입을 호출하여 컴포넌트 내에서 알맞은 의존성을 주입한다.

val appModule = module {

    //Coroutine Dispatcher - Main, IO 를 싱클톤으로 만들어 준다
    single { Dispatchers.Main }
    single { Dispatchers.IO }

    //Repository
    single<TodoRepository> { DefaultRepository(get(), get(), get()) }
	...
    
    //UseCase
    factory { GetToDoListUseCase(get()) }
	...
    
    //viewModel
    factory { ListViewModel(get()) }
    factory { MainViewModel() }
    ...

}

 

 

MyAppKotlinApplication.kt

Application 클래스를 상속받는  MyAppKotlinApplication 클래스를 생성한다.
onCreate() 에서 startKoin { ... } 을 호출하고 모듈을 넘겨준다.

class MyAppKotlinApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        startKoin {
            androidLogger(Level.ERROR)
            androidContext(this@MyAppKotlinApplication)
            modules(appModule)
        }
    }
}

 

 

AndroidManifest.xml

AndroidManifest.xml 에 Application 클래스를 등록해야 한다.
android:name=".MyAppKotlinApplication" 을 통해 만들어준 Application 클래스를 등록한다.

    <application
        android:name=".MyAppKotlinApplication"
		... 
   
    </application>

 

 

MainActivity.kt

Activity 클래스에서 by inject() 를 사용하여 필요한 객체를 주입한다.
inject() 함수를 명시적으로 선언하여 주입을받아 사용한다.

internal class MainActivity : AppCompatActivity() {

	private val viewModel: MainViewModel by inject()
	//private val viewModel by inject<MainViewModel>()
	
    ...

}

 

 

 

감사합니다 :)

728x90
반응형