번거로운 수동 의존성 주입을 실행하는 상용구를 줄이기 위한 Android 용 Jetpack 라이브러리입니다.
이전에는 Dagger, Dagger2를 이용하였으나 현재는 Dagger를 기반으로 빌드한 Hilt를 제공하고 있습니다.
DI를 수동으로 작업하려면 모든 클래스와 종속 항목을 수동으로 구성하고 컨테이너를 사용하여 종속 항목을 재사용 및 관리해야 합니다.
Hilt는 프로젝트의 모든 Android 클래스에 컨테이너를 제공하고 수명 주기를 자동으로 관리함으로써 DI를 사용하는 표준 방법을 제공합니다.
Hilt를 사용하면 다음과 같은 이점을 제공합니다.
이제 힐트를 어떻게 사용하는지, 저의 프로젝트에선 어떻게 적용했는지 알아보겠습니다.
프로젝트의 루트 build.gradle 파일에 다음 코드를 추가합니다.
plugins {
...
id("com.google.dagger.hilt.android") version "2.44" apply false
}
다음으로 app의 build.gradle 파일에 다음 코드를 추가합니다.
plugins {
kotlin("kapt")
id("com.google.dagger.hilt.android")
}
android {
...
}
dependencies {
implementation("com.google.dagger:hilt-android:2.44")
kapt("com.google.dagger:hilt-android-compiler:2.44")
}
// Allow references to generated code
kapt {
correctErrorTypes = true
}
마지막으로 hilt는 자바 8 기능을사용하기 때문에 프로젝트에 자바 8을 사용 설정해주어야 합니다. 만약 되어 있지 않다면 app의 build.gradle 파일에 다음 코드를 작성하시면 됩니다.
android {
...
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
Hilt를 사용하는 모든 앱은 @HiltAndroidApp 어노테이션이 지정된 Application 클래스를 포함하여야 합니다.

이 어노테이션은 Application 수준의 Dependencies 컨테이너 역할을 하는 Application의 기본 클래스 및 Hilt의 코드 생성을 트리거합니다.
생성된 이 Hilt 구성 요소는 Application 객체의 Lifecycle에 연결되며 이와 관련한 Dependencies를 제공합니다. 이는 앱의 상위 구성 요소이므로 다른 구성 요소는 이 상위 구성요에서 제공하는 Dependencies에 접근할 수 있습니다.
위 작업을 마치면 Hilt는 @AndroidEntryPoint 주석이 있는 다른 Android 클래스에 종속 항목을 제공할 수 있습니다.

몇 가지 주의할 사항이 있습니다. 위 어노테이션이 지정되면 이 클래스에 종속된 Android 클래스에도 어노테이션을 지정해야 합니다.
예를 들어 어떤 Activity 안에 Fragment가 존재할 때 Fragment에 어노테이션이 지정된다면 이 Fragment를 사용하는 Activity에서도 어노테이션을 지정해주어야 합니다.
@AndroidEntrPoint 어노테이션은 Android 클래스에 관한 개별 Hilt 구성 요소를 생성합니다. 이러한 구성 요소는 각 상위 클래스에서 종속 항목을 받을 수 있습니다.

구성 요소에서 종속 항목을 가져오려면 @Inject 어노테이션을 사용하여 필드 삽입을 실행합니다.
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
@Inject lateinit var analytics: AnalyticsAdapter
...
}
또한 Hilt가 삽입한 필드는 Private일 수 없습니다. 만약 이를 삽입하려 한다면 컴파일 오류가 발생하게 됩니다.
필드 삽입을 실행하기 위해선 Hilt가 해당 구성요소에서 필요한 종속 항목의 인스턴스를 제공하는 방법을 알아야 합니다. 결합에는 특정 유형의 인스턴스를 종속 항목으로 제공하는 데 필요한 정보가 포함됩니다.
Hilt에 결합 정보를 제공하는 방법 중 생성자 삽입과 모듈을 사용하는 방법이 있습니다. 이 중 모듈을 사용한 방법에 대해 알아보겠습니다.
이 방법은 생성자 삽입이 불가능한 상황에 적용하는 방법입니다. 예로 인터페이스는 생성자 삽입이 불가능합니다. 또한 외부 라이브러리의 클래스와 같이 소유하지 않는 유형도 불가능합니다. 이때 Hilt 모듈을 사용해 Hilt 결합 정보를 제공할 수 있습니다.
Hilt 모듈은 @Module 어노테이션이 지정된 클래스입니다. 또한 @InstallIn 어노테이션을 지정하여 각 모듈을 사용하거나 설치할 Android 클래스를 Hilt에 알려주어야 합니다.

만약 특정 Service가 인터페이스라면 인터페이스는 생성자 삽입이 불가능합니다. 대신 Hilt 모듈 내 @Binds 어노테이션이 지정된 abstract 함수를 생성하여 Hilt에 결합 정보를 제공할 수 있습니다.
interface AnalyticsService {
fun analyticsMethods()
}
// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
class AnalyticsServiceImpl @Inject constructor(
...
) : AnalyticsService { ... }
@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
}
위 코드에서 Hilt가 AnalyticsModule의 종속 항목을 다른 Activity에 삽입하기를 원한다면 Module 클래스에 @InstallIn(ActivityComponent::class) 어노테이션을 지정해주어야 합니다. 이 어노테이션은 Module의 모든 종속 항목을 앱의 모든 Activity에서 사용할 수 있음을 의미합니다.
인터페이스 뿐만 아니라 Retrofit, OkHttpClient 또는 Room Database와 같은 외부 라이브러리 클래스 또는 빌더 패턴으로 인스턴스를 생성해야 하는 경우에도 생성자 삽입이 불가능합니다.
만약 AnalyticsService 클래스를 직접 소유하지 않으면 Hilt 모듈 내에 함수를 생성하고 이 함수에 @Provides 어노테이션을 지정하여 이 유형의 인스턴스를 제공하는 방법을 Hilt에 알릴 수 있습니다.
@Provides 어노테이션이 지정된 함수는 다음과 같은 정보를 Hilt에게 제공합니다.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Provides
fun provideAnalyticsService(
// Potential dependencies of this type
): AnalyticsService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(AnalyticsService::class.java)
}
}
종속 항목과 동일한 타입의 다양한 구현을 제공하는 Hilt가 필요한 경우 Hilt에 여러 결합을 제공해야 합니다. 이때 한정자 @Qualifier를 사용하여 동일한 유형에 대해 여러 결합을 정의할 수 있습니다.
이 어노테이션은 특정 타입에 대해 여러 결합이 정의되어 있을 때 그 타입의 특정 결합을 식별하는 데 사용합니다.

그 다음, Hilt는 각 한정자와 일치하는 유형의 인스턴스를 제공하는 방법을 알아야 합니다. 이 경우 @Provides와 함께 Hilt 모듈을 사용할 수 있습니다.

위의 두 메서드는 동일한 타입의 인스턴스를 제공하지만, 한정자는 두 가지의 서로 다른 결합으로 메서드에 라벨을 지정합니다.

기본적으로 Hilt의 모든 결합은 범위가 지정되어 있지 않습니다. 앱이 결합을 요청할 때마다 Hilt는 필요한 타입의 새 인스턴스를 생성합니다.
그러나 특정 구성 요소로 범위를 지정할 수 있습니다.

Hitl는 결합의 범위가 지정된 구성 요소의 인스턴스마다 한 번만 범위가 지정된 결합을 생성하며, 이 결합에 관한 모든 동일한 인스턴스를 공유합니다.
위 어노테이션을 사용하여 범위를 지정하면 Hilt는 해당 LifeCycle 동안 동일한 인스턴스를 제공합니다. 그러나 범위 지정은 메모리와 관련한 비용 문제로 인해 범위 지정을 최소화 할 것을 권장하고 있습니다.
만약 특정 Activity 뿐만 아니라 App의 모든 위치에서 매번 동일한 인스턴스를 생성해야 한다면, 범위를 SingletonComponent로 지정하는 것이 좋습니다.

다음과 같이 ViewModel 클래스에 @HiltViewModel 어노테이션을 지정합니다. 다음으로 @Inject 어노테이션을 사용하여 제공합니다.
그 다음 @AndroidEntryPoint 어노테이션 지정된 Activity 또는 Fragment에서 ViewModelProvider 또는 by VewModels()를 사용하여 해당하는 ViewModel 인스턴스를 가져올 수 있습니다.

참고 문서