ViewModel을 사용할 때 Factory를 사용하는 경우와 사용하지 않는 경우에는 큰 차이가 있다. 이 차이를 이해하면, ViewModel의 인스턴스 생성 방식과 의존성 주입(DI) 활용 방법을 더욱 효과적으로 설계할 수 있다.
1. 기본적인 ViewModel 사용 (Factory 없이)
✅ 특징
- ViewModel이 기본 생성자만 가진 경우 Factory 없이 사용 가능.
- ViewModelProvider를 사용하여 Activity 또는 Fragment에 간단히 연결 가능.
- 인수가 필요 없는 ViewModel에 적합.
✅ 예제 코드
class MainViewModel : ViewModel() {
val message = MutableLiveData<String>()
fun updateMessage(newMessage: String) {
message.value = newMessage
}
}
class MainFragment : Fragment() {
private val viewModel: MainViewModel by viewModels()
}
✅ 장점
- 간단하고 구현이 쉬움 (by viewModels() 또는 ViewModelProvider 사용 가능).
- 생명주기를 고려하여 자동으로 관리됨.
❌ 단점
- 생성자에서 파라미터를 받을 수 없음 (Repository, UseCase 같은 객체를 직접 주입할 수 없음).
- 의존성 주입(DI)이 어렵고, ViewModel 내부에서 객체를 직접 생성해야 함 (유지보수 어려움).
2. Factory를 사용하는 ViewModel
✅ 특징
- ViewModel 생성자에서 파라미터를 받는 경우 반드시 Factory가 필요함.
- 보통 Repository, UseCase, API 서비스 등의 객체를 ViewModel에 주입할 때 사용.
- DI(Dependency Injection) 환경 (예: Hilt, Dagger)에서 활용하기 좋음.
✅ 예제 코드
① ViewModel이 생성자를 가지는 경우
class MainViewModel(private val repository: DataRepository) : ViewModel() {
val data: LiveData<String> = repository.getData()
}
② Custom ViewModelFactory 구현
class MainViewModelFactory(private val repository: DataRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return MainViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
③ Factory를 이용한 ViewModel 생성
class MainFragment : Fragment() {
private val repository = DataRepository()
private val viewModel: MainViewModel by viewModels {
MainViewModelFactory(repository)
}
}
✅ 장점
- ViewModel의 생성자를 통해 의존성을 주입할 수 있음 (Repository, UseCase 등).
- DI를 활용하여 테스트가 용이하고 유지보수가 쉬움.
❌ 단점
- Factory 클래스를 따로 구현해야 하므로 코드가 길어질 수 있음.
- Factory를 생성할 때 ViewModel을 명시적으로 매핑해야 하는 과정이 필요함.
3. Factory를 사용하지 않는 경우 vs Factory를 사용하는 경우 비교
항목Factory 사용 X (by viewModels())Factory 사용 O (Factory 필요)
사용 가능 조건 | ViewModel에 생성자 파라미터 없음 | ViewModel에 생성자 파라미터 필요 |
ViewModel 생성 방식 | by viewModels() 또는 ViewModelProvider | ViewModelProvider.Factory를 통해 생성 |
의존성 주입(DI) | 불가능 (ViewModel 내부에서 직접 객체 생성) | 가능 (Repository, UseCase 등 주입 가능) |
테스트 용이성 | 어려움 (ViewModel 내부에서 직접 객체 생성) | 쉬움 (의존성을 모킹(Mock)할 수 있음) |
코드의 복잡성 | 단순하고 짧음 | Factory 클래스를 추가해야 함 |
4. ViewModelFactory를 활용하는 고급 예제
(1) ViewModel에 여러 개의 의존성을 주입하는 경우
class MainViewModel(
private val repository: DataRepository,
private val analyticsTracker: AnalyticsTracker
) : ViewModel() {
fun logEvent(event: String) {
analyticsTracker.trackEvent(event)
}
}
(2) Factory에서 여러 개의 파라미터를 전달
class MainViewModelFactory(
private val repository: DataRepository,
private val analyticsTracker: AnalyticsTracker
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return MainViewModel(repository, analyticsTracker) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
(3) Fragment에서 Factory를 활용한 ViewModel 생성
class MainFragment : Fragment() {
private val repository = DataRepository()
private val analyticsTracker = AnalyticsTracker()
private val viewModel: MainViewModel by viewModels {
MainViewModelFactory(repository, analyticsTracker)
}
}
5. Hilt를 이용한 ViewModel Factory 대체 방법
최근에는 DI 라이브러리(Hilt, Dagger)를 사용하면 Factory를 직접 구현할 필요 없음.
(1) Hilt를 활용한 ViewModel 생성
① ViewModel에 @HiltViewModel 추가
@HiltViewModel
class MainViewModel @Inject constructor(private val repository: DataRepository) : ViewModel() {
val data: LiveData<String> = repository.getData()
}
② Repository에 @Inject 추가
class DataRepository @Inject constructor() {
fun getData(): LiveData<String> {
return MutableLiveData("Hello from Repository")
}
}
③ Fragment에서 간단하게 ViewModel 생성
@AndroidEntryPoint
class MainFragment : Fragment() {
private val viewModel: MainViewModel by viewModels()
}
- Hilt를 사용하면 Factory 클래스를 직접 만들 필요 없이, 의존성이 자동으로 주입됨.
- 이 방식이 현재 Android 개발의 표준적인 접근법.
6. 결론
- ViewModel에 생성자 파라미터가 필요 없다면 Factory 없이 by viewModels() 사용.
- ViewModel에 생성자 파라미터가 필요하면 반드시 Factory를 사용해야 함.
- DI 라이브러리(Hilt, Dagger)를 활용하면 Factory 없이도 자동으로 의존성 주입 가능.
- 테스트 가능성과 유지보수를 고려하면 Factory 또는 Hilt를 사용하는 것이 권장됨.
📌 정리:
간단한 ViewModel → by viewModels()
의존성이 있는 ViewModel → Factory 사용 or Hilt 활용
'Android Programming' 카테고리의 다른 글
Modern Anroid Application의 UI 구조 이해 (0) | 2025.03.18 |
---|---|
Modern Anroid Application의 구조 이해 (1) | 2025.03.11 |
Waydroid 설정하기 (0) | 2022.04.15 |