viewModel()이 어떻게 화면별로 ViewModel을 다르게 관리하는지 실제 동작 예제 코드로 보여줘그럼 Navigation Compose 환경에서
두 가지를 비교하는 예제를 만들어 드리겠습니다.
MainScreen → DetailScreen 두 화면이 Navigation Compose로 이동MainViewModel이 MyRepository 의존성을 받음// Repository
class MyRepository {
fun getMessage(screen: String) = "Hello from $screen"
}
// ViewModel
class MainViewModel(private val repository: MyRepository) : ViewModel() {
var message by mutableStateOf("")
private set
fun loadMessage(screen: String) {
message = repository.getMessage(screen)
}
}
// Factory
class MainViewModelFactory(
private val repository: MyRepository
) : 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")
}
}
@Composable
fun AppNavHost(navController: NavHostController) {
val repository = remember { MyRepository() }
val factory = remember { MainViewModelFactory(repository) }
NavHost(navController, startDestination = "main") {
// 외부 주입 방식 (상태 공유)
composable("main") { backStackEntry ->
val vm: MainViewModel = viewModel(
backStackEntry,
factory = factory
)
MainScreen(
viewModel = vm,
onNavigate = { navController.navigate("detail") }
)
}
// 같은 Factory, 같은 ViewModelStoreOwner → 상태 공유됨
composable("detail") { backStackEntry ->
val vm: MainViewModel = viewModel(
backStackEntry,
factory = factory
)
DetailScreen(viewModel = vm)
}
}
}
@Composable
fun MainScreen(viewModel: MainViewModel, onNavigate: () -> Unit) {
Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Main: ${viewModel.message}")
Button(onClick = {
viewModel.loadMessage("MainScreen")
onNavigate()
}) {
Text("Go to Detail")
}
}
}
@Composable
fun DetailScreen(viewModel: MainViewModel) {
Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Detail: ${viewModel.message}")
Button(onClick = { viewModel.loadMessage("DetailScreen") }) {
Text("Change Message")
}
}
}
@Composable
fun AppNavHostWithAnonymousFactory(navController: NavHostController) {
val repository = remember { MyRepository() }
NavHost(navController, startDestination = "main") {
// 내부 생성 방식 (상태 독립)
composable("main") {
val vm: MainViewModel = viewModel(
factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MainViewModel(repository) as T
}
}
)
MainScreen(
viewModel = vm,
onNavigate = { navController.navigate("detail") }
)
}
// Detail 진입 시 새로 생성됨 → Main 상태와 독립
composable("detail") {
val vm: MainViewModel = viewModel(
factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MainViewModel(repository) as T
}
}
)
DetailScreen(viewModel = vm)
}
}
}
| 항목 | Factory 클래스 + 외부 주입 | 익명 객체 + 내부 생성 |
|---|---|---|
| 상태 공유 | 가능 (같은 Factory & Owner) | 기본적으로 불가능 (각 Composable에서 새로 생성) |
| 재사용성 | 높음 | 낮음 |
| 테스트 용이성 | 높음 | 낮음 |
| 코드 길이 | 약간 길어짐 | 짧음 |
💡 결론