[mvvm] 뷰모델 지피티 물음 정리1

말랑돌·2025년 8월 13일

안드로이드 학습

목록 보기
13/17

Navigation Compose에서 viewModel()이 어떻게 화면별로 ViewModel을 다르게 관리하는지 실제 동작 예제 코드로 보여줘

그럼 Navigation Compose 환경에서

  • Factory 클래스를 사용해서 ViewModel을 외부 주입하는 방식
  • 익명 객체 Factory로 Composable 내부에서 생성하는 방식

두 가지를 비교하는 예제를 만들어 드리겠습니다.


📌 예제 시나리오

  • MainScreenDetailScreen 두 화면이 Navigation Compose로 이동
  • MainViewModelMyRepository 의존성을 받음
  • 상태 공유상태 독립 두 가지를 확인

1. ViewModel + Factory 클래스 정의

// 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")
    }
}

2. Navigation Compose 환경 구성

@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)
        }
    }
}

3. Composable 구현

@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")
        }
    }
}

4. 익명 객체 Factory로 내부 생성하는 경우

@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)
        }
    }
}

5. 비교 결과

항목Factory 클래스 + 외부 주입익명 객체 + 내부 생성
상태 공유가능 (같은 Factory & Owner)기본적으로 불가능 (각 Composable에서 새로 생성)
재사용성높음낮음
테스트 용이성높음낮음
코드 길이약간 길어짐짧음

💡 결론

  • 여러 화면에서 상태를 공유하려면 Factory 클래스를 만들고 Navigation의 ViewModelStoreOwner를 지정하는 방식이 맞습니다.
  • 각 화면이 독립된 상태를 가져야 하고 간단한 구조라면 익명 객체로 Composable 내부에서 생성해도 됩니다.

0개의 댓글