Android 공부/의존성 주입
Hilt
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)
}
}