코딩하는 일용직 노동자

Koin 기초 사용법 정리. 본문

안드로이드

Koin 기초 사용법 정리.

bacass 2022. 8. 7. 13:48

#의존성과 의존성주입(Dependency Injection, DI)이란?
class Animal 이 있고, class Cat이 있습니다.
그리고 Cat 에서 Animal 객체를 생성했습니다.
class Animal { ... }

class Cat { 
    val animal = Animal()
}

코드에서처럼 Cat 이 Animal을 멤버로 갖고 있으므로 Cat 은 Animal에 강한 의존성을 가진 관계가 됩니다.

class Cat(private val animal: Animal) { ... }

fun main() {
    val animal = Animal()
    val cat = Cat(animal)
}

이번에는 생성자를 통해 Animal객체를 주입해주었습니다.
이로써 클래스간의 관계를 더 약하게 만들 수 있습니다.
이렇게 클래스 내부에서 객체를 생성하는것이 아니라 외부에서 객체를 생성해서 멤버로 셋팅하는 것을 의존성주입 이라고 합니다.
 
#koin이란?
Koin은 코틀린환경에서 의존성주입을 도와주는 편리한 라이브러리입니다.
의존성주입에 사용되는 dagger, hilt 도 있지만 이들보다 학습과 사용이 쉽다는 장점이 있습니다.

 

Koin - The Kotlin Injection Framework | Koin

Description will go into a meta tag in <head />

insert-koin.io


 
#초기설정
Koin을 사용하기 위해 gradle에 아래와 같이 설정해줍니다.

koin_version= "3.2.0"

// Add Maven Central to your repositories if needed
repositories {
    mavenCentral()
}

// Koin Core features
implementation "io.insert-koin:koin-core:$koin_version"

// Koin main features for Android
implementation "io.insert-koin:koin-android:$koin_version"
// No more koin-android-viewmodel, koin-android-scope, koin-android-fragment

// Java Compatibility
implementation "io.insert-koin:koin-android-compat:$koin_version"
// Jetpack WorkManager
implementation "io.insert-koin:koin-androidx-workmanager:$koin_version"
// Navigation Graph
implementation "io.insert-koin:koin-androidx-navigation:$koin_version"

 
#사용하기
테스트에 사용할 코드입니다.

class Animal {
    fun speek(msg: String) {
        Log.d("Animal", "speek : $msg")
    }
}

class Cat(private val animal: Animal) {
    fun speek() {
        Log.d("Cat", "speek")
        animal.speek("Cat")
    }
}

class MainViewModel : ViewModel() {
    fun doSomthing() {
    }
}​

Koin을 사용하는데 있어서 알아둬야 할 키워드들입니다.
 
startKoin     : Koin을 사용하기 위한 설정 키워드입니다.
modules      : 의존성주입에 사용되는 모듈을 정의합니다.
single            : 싱글톤으로 인스턴스를 생성합니다.
factory         : 사용될 때마다 새로운 인스턴스를 생성합니다.
viewModel  : ViewModel을 정의할때 사용합니다.
get                 : 인스턴스 생성에 필요한 알맞은 객체를 주입해줍니다.


우선 Application 클래스가 있어야 합니다. MyApplication을 만들어줍니다.
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        startKoin {
            // context를 사용.
            androidContext(this@MyApplication)
            // Koin Logger를 사용.
            androidLogger()
            // 모듈을 셋팅해준다.
            modules(appModule)
        }
    }
}

Manifest파일에 MyApplication를 설정해줍니다.
<application
    android:name=".MyApplication"
    ... >

#module 만들기
다음으로는 module을 만들어줍니다.
의존성주입에 사용되는 클래스들이 이곳에서 설정됩니다.

val appModule = module {
    single { Animal() }
    factory { Cat(get()) }
    viewModel { MainViewModel() }
}

코드에 보면 Cat의 생성자에 get()으로 처리한 것을 볼 수 있습니다.
Cat의 생성자는 Animal 타입의 객체를 인자로 받게 되어 있는데
그 윗 라인에서 Animal을 이미 생성해서 알고있기 때문에 get()으로 넘겨줄 수 있는것입니다.
이렇게 get()은 의존성주입에 필요한 인자를 자동으로 매핑시켜줍니다.

 
#의존성주입 받기
이제 설정이 끝났으니 소스코드에서 사용해 볼 수 있습니다.

import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel

class MainFragment : Fragment() {
    companion object {
        fun newInstance() = MainFragment()
    }

    private val viewModel: MainViewModel by viewModel()

    private val cat2: Cat by inject()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        Log.w("MainFragment", "cat =================================================")
        val cat = get<Cat>()
        cat.speek()

        Log.w("MainFragment", "cat2 =================================================")
        cat2.speek()

        Log.w("MainFragment", "viewModel =================================================")
        viewModel.doSomething()
    }
}

cat은 get()키워드를 이용해서 non-lazy 하게 주입받아 사용할 수 있습니다.

cat2는 by inject() 키워드를 이용해서 lazy하게 주입받아 사용할 수 있습니다.

ViewModel의 경우 주입받는 방법이 다릅니다.
module에서 viewModel 키워드를 이용해 설정을 시키면 소스코드 내에서는 by viewModel() 키워드를 사용해 주입받습니다.
 
만약 Activity_A가 있고 그 안에 Fragment_A와 Fragment_B가 있을때 하나의 ViewModel을 함께 공유할 수 있습니다.
Activity_A에서 by viewModel() 로 주입을 받고 Fragment_A와 Fragment_B에서는 by sharedViewModel() 키워드를 이용해서 주입을 받으면 Activity_A의 ViewModel을 받아와 함께 이용할 수 있습니다.
하나의 ViewModel을 이용하는 것이기 때문에 데이터가 변하면 LiveData를 통해 모든 뷰가 이벤트를 받을 수 있습니다.

실행시킨 후 로그를 보면 정상적으로 작동되는것을 확인 할 수 있습니다.

 
#마무리
Koin을 이용해 적용하는 방법을 알아봤습니다.
아주 기초적인 방법만 설명해 놓았으니 더 심화된 내용은 공식 사이트나 블로그를 참조해주십시오.