[Android] DI(의존성 주입)과 Koin에 대하여

2019. 11. 15. 00:01Android

반응형

#오늘의 개념

: Koin

 

DI 라이브러리란?

의존성 주입은 한 개체가 다른 개체의 의존성을 제공하는 기술입니다. "의존성"은 예를 들어 서비스로 사용할 수 있는 개체입니다. 사용할 서비스를 지정하는 클라이언트 대신 클라이언트가 사용할 서비스를 알려줍니다.  -위키백과

우선 DI 란?

DI : Dependency Injection(의존성 주입)의 준말

의존성 주입의 의미를 알아보자.

 

의존성이란, 나는 의존성의 관계를 아래와 같이 이해한다.

예를 들어, A와 B가 있다. 어떤 action이 행해져 A에 영향을 미쳤다.  A가 영향을 받았기 때문에 B도 영향을 받는다. 

이러한 A-B의 관계를 의존성이 있다고 본다.

 

주입이란, "밖에서 안으로 넣는다"라는 의미로 볼  수 있다. 

 

즉, 소프트웨어에서의 의존성 주입이란, 의존성 있는 객체를 외부에서 넣어준다는 의미다.

 

 

예를 들어, 아래와 같은 객체들이 있다고 하자.

data class Parent(
    val child_a : ChildA,
    val child_b : ChildB,
    val child_c : ChildC,
    val child_d : ChildD
)

data class ChildA()
data class ChildB()
data class ChildC()
data class ChildD()

 

Parent객체를 생성하는 방법은 아래와 같이 2가지다. 

// DI 라이브러리를 사용하지 않을 때.
val parentObject = Parent(ChildA(), ChildB(), ChildC(), ChildD())

// DI 라이브러리(Koin)를 사용할 때.
// 이게 끝이다! WOW! 훨씬 간단하다.
val parentObject:Parent by inject()

 

DI 라이브러리를 사용하면, 사용하지 않을 때처럼 Parent생성자 내 파라미터 객체를 일일이 생성하지 않아도 된다는 것이다!

위 코드에서 사용한 inject()는 Parent 내 파라미터 객체를 외부에서 '주입' 해준다는 의미다. Koin 사용법은 아래에서 설명하겠다 :)

 

 

DI 라이브러리가 필요한 이유는?

  1. 코드의 재사용성
  2. 리팩토링의 용이성
  3. 테스트의 용이성
  4. 객체의 생성과 사용의 분리 가능. 따라서 A객체에 문제가 생겼을 경우, B객체에 미치는 영향을 방지할 수 있음.

 


Koin 이란?

A pragmatic lightweight dependency injection framework for Kotlin developers. Written in pure Kotlin using functional resolution only: no proxy, no code generation, no reflection! - Koin 공홈

100% Kotlin으로 짜인 경량의 DI 라이브러리이다. 

Koin Android 공식 홈페이지에 문서정리가 잘되어있으니 꼭 참고하길 바란다.

 

 

Koin 예제 코드는 아래 Github에서 확인 가능

https://github.com/nurisis/ConcertInfoProject

 

nurisis/ConcertInfoProject

A sample android app that shows how to use MVVM pattern and various libraries in Kotlin by Clean Architecture. - nurisis/ConcertInfoProject

github.com

 

 

Koin 사용법

1. Dependency 추가

아래에 있는 것들을 다 추가할 필요는 없고, 필요에 따라 추가해서 쓰면 된다.

필자의 경우, 예제 샘플에서 위 2개만 사용하였다. (koin-core, koin-viewmodel)

 

build.gradle

ext {
    koin_version = '2.0.1'
}

dependencies {
    // Koin for Kotlin
    implementation "org.koin:koin-core:$koin_version"
    
    // Koin AndroidX ViewModel features
    implementation "org.koin:koin-androidx-viewmodel:$koin_version"
    
    // Koin for Android
    implementation "org.koin:koin-android:$koin_version"
    
    // Koin for Unit tests
    testImplementation "org.koin:koin-test:$koin_version"
    
    // Koin for Java developers
    implementation "org.koin:koin-java:$koin_version"
    
    // Koin AndroidX Scope features
    implementation "org.koin:koin-androidx-scope:$koin_version"
}

 

 

2. Koin module 생성

AppModule.kt

val appModule = module {
 
   single { SaveBookMarkUseCase(get()) }
   single { DeleteBookMarkUseCase(get()) }

   // 아래는 factory의 사용도 보여주기 위해 쓴 임시 코드
   factory { SaveBookMarkUseCase(get()) }
 
}

 

위에서 쓰인 용어 정리

  • module : Koin 모듈을 정의할 때 사용
  • single : 앱이 살아있는 동안 전역으로 사용할 수 있는 오직 한 개의 객체 생성. static 변수와 같은 개념으로 볼 수 있음.
  • factory : inject() 할 때마다 해당 객체를 생성
  • get() : get() 자리에 들어갈 파라미터 객체를 알아서 주입해줌.
    • 예를 들어, SaveBookMarkUseCase(val repository:Repository) 라면, 해당 객체 안에는 Repository() 객체가 들어가야 함. module에서 이 부분을 그냥 get()으로 넣어놓으면, 파라미터에 알맞은 객체를 알아서 주입해줌. 

 

 

ViewModel 객체일 경우 아래와 같이 viewModel {}을 사용하면 된다.

ViewModelModule.kt

val viewModelModule = module {
    viewModel { ConcertViewModel(get(),get(),get(),get()) }
}

 

 

3.  Application 파일 생성 및 추가

MyApp.kt

class MyApp : Application() {

    override fun onCreate() {
        super.onCreate()

	// 위에서 생성한 module들을 아래와 같이 추가해준다.
        startKoin {
            androidContext(this@MyApp)
            modules(listOf(viewModelModule, netWorkModule, appModule))
        }
    }
}

 

MyApp.kt를 생성한 후 반드시 manifest.xml에  아래와 같이 name에 추가해줘야 한다!

<application
        android:name="com.nurisis.concertinfoproject.MyApp"
        ...>
    </application>

 

여기까지 잘 따라왔다면, Koin을 쓸 준비가 되었다.

 

 

4. Last! 코드에서 사용하기 🤓

//****************************
// 객체 inject - 이 부분은 예제코드에는 없지만 설명하기 위해 여기에 추가함
private val listAdapter: ConcertListAdapter by inject()

// 일반 viewModel inject
private val concertViewModelActivity by viewModel<ConcertViewModel>()

// Activity에서 생성한 viewModel을 Fragment에서 공유해서 사용하고 싶은 경우
private val concertViewModelFragment by sharedViewModel<ConcertViewModel>()

 

위의 코드를 설명하자면,

우선 viewModel객체의 경우, concertViewModelActivity와 concertViewModelFragment는 동일한 객체이다. 왜냐하면 sharedViewModel로 불러왔기 때문. 굉장히 유용하다👍

 

또한 ConcertViewModel을 외부 주입으로 불러왔기 때문에, ConcertViewModel 생성자에 들어가는 파라미터 객체를 일일이 코드에서 넣어주지 않아도, 바로 concerViewModelActivity로 사용할 수 있다. 엄청난 편리함!!!

 

이게 끝이다. 정말 간편하지 않은가?

 

 

 

이렇게 해서 Koin 사용법을 알아보았다. 드디어 끝!!!!!!!!

 

 

 

글에서 틀린 내용이나 나누고 싶은 내용이 있으시다면 자유롭게 피드백 주시면 감사하겠습니다🤓 

또한 위 예제 코드가 도움이 되셨다면 깃 헙의 Star를 눌러주세요!🤓🤓🤓🤓

 

 

 

참조

https://developer.android.com/training/dependency-injection

https://en.wikipedia.org/wiki/Dependency_injection 

https://en.wikipedia.org/wiki/Boilerplate_code

https://beomseok95.tistory.com/188

반응형