Hilt는 타입으로 의존성을 구분한다.
의존성이 Hilt 컴포넌트에 바인딩될때, 의존성을 식별할 수 있는 식별자로 class의 패키지명을 포함하고 있는 canonical name을 식별자로 쓰고있다. 어떤 의존성이 컴포넌트에 바인딩이 되고 바인딩된 의존성을 클라이언트가 컴포넌트에 요청할 때 정확하게 구분하고 수행할 수 있는 이유는 어노테이션이 마킹된 위치에 해당 의존성 타입이 명시되어 있기 때문이다.
그렇다면 동일한 타입이 두 번 바인딩될 경우는 어떻게 될까?
Hilt입장에서는 클라이언트가 의존성을 요청할 때 어떤 의존성을 주입을 해야할지 모른다. 컴파일 타입에 object 그래프를 점검하고 중복 바인딩 문제가 있는 경우 컴파일을 중단한다. 그리고 DuplicateBindings 이라는 중복 바인딩 에러가 뜬다. 그렇지만 우리는 동일한 타입을 쓰고 중복 바인딩을 유지하면서 바인딩을 구분해서 쓰고싶은 경우도 있다.
@Qualifiers
같은 타입의 여러 인스턴스를 제공해야 할 경우, @Qualifier 어노테이션을 사용하여 구분할 수 있다.
Qualifiers를 직접 사용하는 것은 불가능하고 애노테이션을 갖는 새로운 annotation 클래스를 생성해야 한다. 그리고 바인딩하는 곳에 함께 선언해주면 된다. SomeDependency를 반환하는 두 개의 Provides 메소드가 있음에도 두 개를 식별해놓았기 때문에 Hilt가 맞게 주입을 할 수 있는 것이다.
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class FirstQualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class SecondQualifier
@Module
@InstallIn(SingletonComponent::class)
object FooModule {
@FirstQualifier
@Provides
fun provideFirstImplementation(): SomeDependency {
return FirstDependencyImpl()
}
@SecondQualifier
@Provides
fun provideSecondImplementation(): SomeDependency {
return SecondDependencyImpl()
}
}
주입받을 때는 다음과 같이 사용할 수 있다.
import javax.inject.Inject
class Foo @Inject constructor(
@FirstQualifier val firstDependency: SomeDependency,
@SecondQualifier val secondDependency: SomeDependency
) {
...
}
@Named
@Qualifiers보다 쉽게 사용할 수 있는 방법으로, 유니크한 key값을 갖는 문자열을 입력하여 구분할 수도 있다.
@Module
@InstallIn(SingletonComponent::class)
object FooModule {
@Named("FirstQualifier")
@Provides
fun provideFirstImplementation(): SomeDependency {
return FirstDependencyImpl()
}
@Named("SecondQualifier")
@Provides
fun provideSecondImplementation(): SomeDependency {
return SecondDependencyImpl()
}
}
일반적인 의존성 주입의 메커니즘을 다시 살펴보자.
client <- component <- 주입할 객체
client는 component에게 의존성 주입을 요청을 할 것이고, component는 주입할 객체를 찾아 바인딩할 것이다. 그리고 한 번 주입받은 객체는 여러번 참조해도 처음 주입받은 객체와 같다.
이 때 주입할 객체의 초기화를 늦춘다거나 처음 주입받은 객체와 다른 객체를 사용하고 싶을 경우 사용하는 Injection이 있다.
Lazy
객체를 초기화하는 것을 늦추고 싶다면 Lazy Injection을 사용할 수 있다. 바인딩 될 타입을 포함한 Lazy<T> 을 만들어주면 된다. Lazy 인스턴스 자체는 컴포넌트별 정해진 의존성 주입이 되는 시기에 이루어진다. 하지만 Lazy의 바인딩 될 제너릭타입에 있는 <T> 는 아직 주입 되지 않은 상태이다. 가져오기 위해서는 lazy의 get메소드로 호출해야 되고, 호출 하는 이때 바로 <T> 객체가 생성이 된다.
Lazy<T>의 get() 호출 이후 다시 get() 을 호출하면 동일한(캐시된) T 인스턴스를 얻는다. 또한 T 바인딩에 Scope가 지정되어 있다면, 각 Lazy<T> 요청에 대한 동일한 Lazy<T> 인스턴스가 주입된다. 특정 시점에 바인딩 인스턴스화 할 때나 인스턴스 생성에 비용이 큰 경우 사용하면 좋다.
Provider
Provider Injection은 Lazy Injection과 마찬가지로 주입받고자 하는 대상을 제너릭 타입으로 주입 받는다. 다른 점은, 매 get() 호출마다 새로운 인스턴스 T를 반환한다. 하지만 Scope가 지정되어 있다면, 동일한 인스턴스를 반환하게 된다. 하나의 Provider<T>로 여러 T 인스턴스를 생성하길 원할 때 사용할 수 있다.
참고자료
* https://fastcampus.co.kr/dev_online_dihilt
'🗡️Hilt' 카테고리의 다른 글
[Hilt] Dagger Hilt로 의존성 주입하기 (0) | 2024.06.22 |
---|---|
[Hilt] Hilt 내부 동작 원리 (0) | 2024.04.10 |