본문으로 바로가기
반응형

cr이글은 Dagger 2 v2.25.2 를 기반으로 설명하며, Kotlin으로 예제 코드를 설명합니다.

또한 공식 Dagger site를 참고하였습니다.

https://dagger.dev/dev-guide/


이 글은 Dagger 2에 대한 포스팅중 하나로 아래의 포스팅을 먼저 읽고 오시길 추천드립니다.

2020/06/01 - [개발이야기/Kotlin] - [Dagger 2] Dependency injection, Dagger2 사용 - 기초 #1

2020/06/01 - [개발이야기/Kotlin] - [Dagger 2] Qualifier, instance binding 사용, @Named, @BinsInstance #2


Provider Injections

지금까지 생성했던 객체들은 하나의 객체를 생성해서 채워주는 형태로만 사용했습니다.
constructor에 inject하는 경우 들어갈 인자들을 하나씩 생성해서 채워주거나, constructor를 이용하여 객체 하나를 생성했습니다.

또한 멤버 변수의 객체를 채워주는 기존의 작업들은 하나의 instance만 생성해서 dagger가 채워주는 형태입니다.

다만 dagger를 통해서 생성되는 객체가 여러개가 필요하다면 객체를 생성해 주는 Provider 자체를 받아올 수 도 있습니다.


class RichHero @Inject constructor(private val person: Person) {

    val weapons = mutableListOf()
}

interface Weapon {
    fun type(): String
}

class Suit : Weapon {
    override fun type() = "수트"
}


먼저 RichHero라는 class를 만듭니다.
이 클래스는 weapon을 list로 여러개 가질 수 있습니다.

그리고 WeaponSuit는 이전 포스팅에서 사용했던것을 재사용 합니다.


이제 Module을 준비해 봅니다.
@Module
class RichHeroModule {
    @Provides
    fun provideRichHero(person: Person) = RichHero(person)

    @Provides
    fun provideSuit(): Weapon = Suit()
}


이전의 글들을 이해했다면 가장 기본적인 Module의 형태 입니다.

RichHero 객체 차제를 생성해 주는 Provider와 Suit 객체를 생성해 주는 provider를 각각 만듭니다.


@Component(modules = [RichHeroModule::class])
interface RichHeroComponent {

    fun callRichHero(): RichHero

    fun getWeapon(): Weapon

    fun inject(main: Main)

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance person: Person): RichHeroComponent
    }
}


Component에는 RichHero 객체 자체를 생성하는 함수, 무기를 생성하는 함수, 그리고 Main 클래스에 Inject하기 위한 함수로 구성 됩니다.

이번에는 Module에 Person 객체를 생성하는 Provider를 만들지 않았습니다.

대신 Caller에서 Dagger로 RichHero 객체를 생성할때, Person 인자는 직접 생성해서 넘겨주도록 하기 위해서 @Component.Factory를 사용해 create() 함수를 재정의 했습니다.

(이전 포스팅에서 @Component.Builder는 사용해 봤으니, 이번에는 @Component.Factory를 사용해 봅니다.)


이제 component와 Module이 준비 되었으므로 rebuild를 한번 수행 합니다.

하지만 여기서는 Main함수를 아직 정의하지 않았으니 빌드 오류가 납니다.

(아니면 Main함수 껍데기만이라도 만들어 놓고 빌드를 해야 합니다.)


그럼 호출하는 부분인 Main class를 구성해 보겠습니다.

import javax.inject.Inject
import javax.inject.Provider

class Main {
    @Inject
    lateinit var weaponProvider: Provider<Weapon>

    fun getRichHero() {
        val person = IronMan()

        val richHeroComponent = DaggerRichHeroComponent.factory().create(person)
        richHeroComponent.inject(this)

        val richHero = richHeroComponent.callRichHero()

        (1..10).forEach { _ ->
            val weapon = weaponProvider.get()
            richHero.weapons.add(weapon)
        }
    }
}


DaggerRichheroComponentcreate() 하면서 생성된 IronMan 객체를 인자로 넘겨 줍니다.

그런 이후에DaggerRichheroComponent.inject(this) 함수를 호출하여 (this == Main) @Inject 필드를 채우도록 합니다.

이때 injection 해야 할 type이 Provider<T> 형태 입니다.

따라서 Dagger는 weapon을 생성하는 Provider 자체를 weaponProvider 멤버변수에 inject 시켜줍니다.


그리고 weaponProvider.get() 함수를 이용하면 계속해서 새로 생성된 weapon 객체를 전달 받을 수 있습니다.


여기서는 자동완성된 코드를 넣지는 않지만 이전 포스팅의 Module 자동완성 코드를 보면 get()이 어떻게 값을 반환하는지 알 수 있습니다.

(갑자기 get()이 어디서 튀어나왔는지도..)


Lazy injections

특정 object를 injection할때 필요에 따라 lazy하게 생성하는 경우가 있습니다.

사용한 Main 클래스를 좀 수정해 보겠습니다.
class Main {
    @Inject
    lateinit var lazyWeapon: Lazy<Weapon>

    fun getRichHero() {
        val person = IronMan()

        val richHeroComponent = DaggerRichHeroComponent.factory().create(person)
        richHeroComponent.inject(this)

        val richHero = richHeroComponent.callRichHero()

        (1..10).forEach { _ ->
            val weapon = lazyWeapon.get() //이 순간 lazyWeapon의 객체가 생성되어 채워 집니다.
            richHero.weapons.add(weapon)
        }
    }
}


weaponProvider 함수를 lazyWeapon으로 변경했습니다.

Lazy<T>를 사용하면 객체의 생성을 get()함수의 호출까지 지연시킬수 있습니다.

첫번째 get()시점에 객체가 생성되며, 이후에 get()을 호출할 경우 동일한 객체가 반환됩니다


반응형