Hilt 는 Compile 시점에서 의존성 주입 패턴 코드를 자동으로 생성해주기에 보일러플레이트 코드가 감소하고 모듈, 코드 간 커플링(결합도) 를 낮춰 준다.
하지만 객체를 생성할 대때, 우리는 컴파일 시점이 아닌 런타임 시점에 특정 데이터를 전달해줘야 하는 경우가 있다.
예를 들어, 서버로부터 내려온 유저 데이터 같은 경우는 실시간마다 값이 바뀔 수 있기에 컴파일 시점에서 알 수 없다.
그렇기에 Dagger Hilt 에는 의존성 주입과 함께 런타임 시점 데이터를 주입을 위해 Assisted 와 AssistedInject 라는 Annotation 을 제공한다.
이 부분은 읽지 않아도 되지만, 과거 Hilt 를 사용하며 AssistedInject 를 사용하기 위해 얼마나 귀찮은 과정을 거쳤는지 간단하게 알아보려고 한다.
과거에는 개발자가 직접 Factory 코드를 생성해줘야했다.
class MainViewModel @AssistedInject constructor(
@Assisted val userId: Int,
private val getUserUseCase: GetUserUseCase
) : ViewModel() {
@AssistedFactory
interface MainViewModelFactory {
fun create(userId: Int): MainViewModel
}
private val _count = MutableStateFlow(0)
val count = _count.asStateFlow()
fun increase(){ _count.getAndUpdate { it + 1 } }
companion object {
fun provideFactory(
assistedFactory: MainViewModelFactory,
userId: Int
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return assistedFactory.create(userId) as T
}
}
}
}
예를 들어 위와 같이 Runtime 에 공급받을 userId 라는 파라미터가 존재하는 경우, 이것 하나를 위해 Factory 를 생성하는 코드를 구현하고, 이것을 viewModel 객체를 생성할 때 직접 사용해야했다.
이와 같은 방식은 보일러플레이트 코드가 생성되며, 추후 관리의 어려움으로 이어졌다.
위와 같이 불편했던 방식은 Hilt 2.49 부터 매우 간편하게 변경되었다.
이전에는 AssistedInject 를 사용하기 위해서는 hiltViewModel 을 사용하면 안되었지만, 이제는 이를 사용해도 되고, 구현 방법에는 애초에 이를 사용해야한다.
@HiltViewModel(assistedFactory = MainViewModel.MainViewModelFactory::class)
class MainViewModel @AssistedInject constructor(
@Assisted val userId: Int,
private val getUserUseCase: GetUserUseCase
) : ViewModel() {
@AssistedFactory
interface MainViewModelFactory {
fun create(userId: Int): MainViewModel
}
private val _count = MutableStateFlow(0)
val count = _count.asStateFlow()
fun increase(){ _count.getAndUpdate { it + 1 } }
}
한 눈에 보더라도 코드의 양이 엄청나게 줄고, HiltViewModel 이 사용하는 assistedFactory 가 무엇인지 한 눈에 확인할 수 있게 되었다.
현재 이를 구현하는 방법은 다음과 같다.
val viewModel = hiltViewModel { factory: MainViewModel.MainViewModelFactory ->
factory.create(2)
}