posite 2026. 2. 23. 15:30

Hilt

  • Dagger를 구글이 Android 전용으로 다듬은 의존성 주입을 자동화해주는 라이브러리
  • Android 생명주기에 알맞는 컴포넌트가 정의되어 있음 - SingletonComponent, ActivityComponent
  • Scope 관리 유용 : @Singleton, @ActivityScoped 등 쉽게 Scope 지정 가능
    • SingletonComponent : 앱 실행 ~ 종료
    • ActivityRetainedComponent : Activity 첫 생성 ~ 최종 종료(화면 회전에서 사라지지 않음)
    • ActivityComponent : Activity 생성 ~ 파괴(화면 회전 시 사라짐)
    • FragmentComponent : Fragment 생성 ~ 파괴
    • ViewModelComponent : ViewModel 생성 ~ 파괴
  • ViewModel, Navigation 등 Jetpack 라이브러리들과 호환성 높음
  • 컴파일 타임 에러 검출 : 의존성 주입 오류를 빌드 시점에서 감지하여 런타임 에러 방지
  • 코드 생성 방식을 사용하여 빌드 시간이 길어질 수 있음
  • 복잡한 설정을 어노테이션으로 대체하여 구현
    • @HiltAndroidApp : Hilt의 코드 생성기를 트리거함  앱이 종료되기 전까지 의존성을 관리하는 SigletonComponent가 이곳에 생성됨
    • @Module :  외부 라이브러리, 인터페이스등 생성자로 만들 수 없는 외부 객체를 정의하는 어노테이션
    • @InstallIn : 모듈이 어떤 컴포넌트에 설치될 지 지정함
    • @Provides : 메서드가 리턴하는 객체를 Hilt가 관리하게 하는 어노테이션 - 외부 라이브러리 객체 혹은 빌더 패턴, 인스턴스를 생성하는 로직이 필요할 때 사용
    • @Singleton : 해당 객체를 한 번만 생성하고 재사용하게하는 어노테이션
    • @Inject contstructor() : 해당 객체 생성 시 Hilt에게 필요한 객체를 생성해 달라고 요청하는 어노테이션
    • @HiltViewModel : ViewModelFactory를 자동으로 생성하여 ViewModel 주입해주는 어노테이션
    • @AndroidEntryPoint : Activity, Fragment 등에 붙이며 Hilt를 통해 의존성 주입을 받음을 표시하는 어노테이션 없으면
      by viewModels() 작동 안됨
    • @Binds : 인터페이스와 구현체를 연결해주는 어노테이션 - 인터페이스를 주입해야 할 때 사용
    • @Qualifier : 서로 다른 구현체의 종류를 구별하기 위해 정의하는 어노테이션
    • @Named : 문자열로 객체를 구분하는 어노테이션 - 안정성이 낮음

 

예시)

Application 설정

@HiltAndroidApp
class MyAwesomeApp : Application()

 

 

Module 설정

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    @Singleton
    fun provideApiService(): ApiService {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .build()
            .create(ApiService::class.java)
    }
}



Repository : @Inject constructor() 설정

class UserRepository @Inject constructor(
    private val apiService: ApiService // 위 Module에서 제공한 객체가 주입됨
) {
    fun getUserName() = "Hilt Master"
}

 

 

Repository가 interface, Impl로 구성된 경우 : 주입해 줄 Module이 필요하며, @Binds를 통해 주입

interface UserRepository {
    fun getUserName(): String
}

class UserRepositoryImpl @Inject constructor(
    private val apiService: ApiService
) : UserRepository {
    override fun getUserName() = "Hilt with Interface"
}

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {

    @Binds
    @Singleton
    abstract fun bindUserRepository(
        userRepositoryImpl: UserRepositoryImpl
    ): UserRepository
}

 

 

interface의 구현체가 2개인 경우 : @Qualifier로 어노테이션 정의 및 주입

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class RemoteRepository

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class LocalRepository

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {

    @RemoteRepository
    @Binds
    @Singleton
    abstract fun bindRemoteRepo(impl: RealUserRepository): UserRepository

    @LocalRepository
    @Binds
    @Singleton
    abstract fun bindLocalRepo(impl: FakeUserRepository): UserRepository
}



ViewModel 설정 : @HiltViewModel, @Inject constructor() 설정함  UserRepository의 구현체가 여러개인 경우, 원하는 구현체의 어노테이션 지정

@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel() {
    val userName = repository.getUserName()
}


//Remote
@HiltViewModel
class UserViewModel @Inject constructor(
    @RemoteRepository private val repository: UserRepository
) : ViewModel() {
    // ...
}

//Local
class DebugViewModel @Inject constructor(
    @LocalRepository private val repository: UserRepository
) : ViewModel() {
    // ...
}

 

 

Activity 설정 : @AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        println(viewModel.userName)
    }
}