오늘은 의존성 주입(DI, Dependency Injection) 도구인 Hilt를 사용하는 방법을 알아보겠습니다.
🎯 Hilt 추가하기
- 의존성 설정
Hilt 라이브러리와 컴파일러를 추가합니다.
// libs.versions.toml 파일 [versions] hilt = "2.56.2" [libraries] hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } [plugins] google-dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } // build.gradle.kts plugins { alias(libs.plugins.google.dagger.hilt.android) } dependencies { implementation(libs.hilt.android) ksp(libs.hilt.android.compiler) }
+ ksp 의존성 설정
// libs.versions.toml 파일 [versions] ksp = "2.1.20-2.0.1" [plugins] google-devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } // build.gradle.kts plugins { alias(libs.plugins.google.devtools.ksp) }
🎯 Hilt 초기 설정하기
- Application
Hilt를 사용하기 위해서는 Application 클래스가 필요합니다. 프로젝트 생성 시 자동으로 생성되지 않기 때문에 직접 추가해야 합니다.
// MyApplication 파일 @HiltAndroidApp class MyApplication : Application() // AndroidManifest.xml 파일 <application android:name=".MyApplication" // ...
MyApplication은 클래스명이며 자유롭게 변경 가능합니다. Application 클래스를 생성해 @HiltAndroidApp을 붙이고, AndroidManifest.xml의 <application> 태그에 name 속성을 추가합니다.
만약 Application()을 찾을 수 없다면 아래의 라이브러리를 추가합니다.
// libs.versions.toml [versions] lifecycleViewModelCompose = "2.9.1" [libraries] androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewModelCompose" } // build.gradle.kts dependencies { implementation(libs.androidx.lifecycle.viewmodel.compose) }
- Activity
Activity 클래스에는 @AndroidEntryPoint를 붙입니다.
// MainActivity 파일 @AndroidEntryPoint class MainActivity : ComponentActivity() { // ... }
- ViewModel
ViewModel 클래스에는 @HiltViewModel을 붙이고 @Inject를 추가해 생성자 파라미터를 주입받도록 설정할 수 있습니다.
@HiltViewModel class MainViewModel @Inject constructor( private val repository: MainRepository ) : ViewModel() { // ... }
- Binding
Hilt가 의존성 주입을 하기 위해서는 어떤 타입의 의존성을 어떤 방식으로 제공할지에 대한 정보가 필요합니다. 이러한 정보를 결합(Binding) 정보라 합니다.
결합 정보를 제공하는 방법 중 하나로 생성자 삽입(Constructor Injection)이 있습니다. 생성자 삽입은 클래스의 생성자에서 @Inject를 사용해 인스턴스를 제공하는 방법을 Hilt에 알리는 방법입니다.
다음과 같이 AnalyticsAdapter의 인스턴스를 제공하는 방법을 생성자 삽입을 사용해 Hilt에 알릴 수 있습니다.
- AnalyticsAdapter의 의존성인 AnalyticsService의 인스턴스를 제공하는 방법도 Hilt에 알려야 합니다.
class AnalyticsAdapter @Inject constructor( private val service: AnalyticsService ) { ... }
- Module
생성자 삽입은 생성자가 있는 클래스만 가능하고, 외부 라이브러리 클래스에는 사용할 수 없습니다. 이를 해결하기 위해 Module을 사용해 Hilt에 결합 정보를 제공할 수 있습니다.
@Binds를 사용해 Interface의 인스턴스를 삽입할 수 있고, @Provides를 사용해 외부 라이브러리 클래스(Retrofit, OkHttpClient, Room 등) 같은 소유하지 않은 타입의 인스턴스를 삽입할 수 있습니다.
- @Binds: 반환 타입으로 어떤 인터페이스의 인스턴스를 제공할지와, 파라미터로 제공할 구현을 Hilt에 알립니다.
interface AnalyticsService { fun analyticsMethods() } class AnalyticsServiceImpl @Inject constructor( ... ) : AnalyticsService { ... } @Module @InstallIn(ActivityComponent::class) abstract class AnalyticsModule { @Binds abstract fun bindAnalyticsService( analyticsServiceImpl: AnalyticsServiceImpl ): AnalyticsService }
- @Provides: 반환 타입으로 어떤 타입의 인스턴스를 제공할지와, 파라미터로 해당 타입의 의존성을, 함수 본문으로 해당 타입의 인스턴스를 제공하는 방법을 Hilt에 제공합니다. 따라서 해당 타입의 의존성 주입이 필요할 때마다 Hilt는 함수 본문을 실행하게 됩니다.
@Module @InstallIn(SingletonComponent::class) object DatabaseModule { @Provides @Singleton fun provideDatabase(@ApplicationContext context: Context): AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, "crosswalk_timer.db").build() @Provides fun provideCrosswalkDao(db: AppDatabase): CrosswalkDao = db.crosswalkDao() }
- @Singleton을 사용해 싱글톤 패턴으로 DB 재생성을 방지합니다.
이렇게 해서 Hilt에 대해 알아보았습니다.