[Android] NavBackStackEntry를 활용한 ViewModel 공유

이주형·2025년 1월 25일

Jetpack Compose를 사용하여 Android 앱을 개발할 때, 여러 화면에서 데이터를 공유해야 하는 경우가 많습니다. 특히, ViewModel을 사용하여 UI 상태를 관리할 때, 화면 간에 ViewModel 인스턴스를 어떻게 공유할지가 중요한 문제가 됩니다. 이 글에서는 Jetpack Compose Navigation에서 NavBackStackEntry를 활용하여 ViewModel을 공유하는 가장 효과적이고 권장되는 방법을 자세히 설명하겠습니다.

📌 왜 ViewModel 공유가 필요할까요?

ViewModel은 UI 관련 데이터를 저장하고 관리하는 데 사용되는 아키텍처 구성 요소입니다. ViewModel을 사용하면 UI 로직과 데이터 로직을 분리하여 코드를 더 깔끔하고 유지 보수하기 쉽게 만들 수 있습니다.
하지만, 앱이 여러 화면으로 구성되어 있을 때, 각 화면마다 독립적인 ViewModel 인스턴스를 생성하면 데이터 공유가 어려워집니다. 예를 들어, 타이머 앱에서 타이머 설정 화면과 타이머 실행 화면이 있다고 가정해 보겠습니다. 타이머 설정 화면에서 설정한 시간을 타이머 실행 화면에서 사용하려면, 두 화면이 동일한 ViewModel 인스턴스를 공유해야 합니다.

📌 ViewModel 공유 방법: NavBackStackEntry 활용

Jetpack Compose Navigation에서 ViewModel을 공유하는 방법은 여러 가지가 있지만, 그중에서도 NavBackStackEntry를 활용하는 방법이 가장 권장됩니다. 이 방법은 다음과 같은 장점을 제공합니다.

  • 간결함: 코드가 비교적 간결하고 이해하기 쉽습니다.
  • 유연성: NavGraph 내의 여러 화면에서 ViewModel을 공유할 수 있습니다.
  • 안전성: NavGraph의 ViewModelStoreOwner를 사용하므로, ViewModel의 생명주기가 NavGraph와 일치하여 안전하게 관리됩니다.
  • 공유범위: NavGraph내에서만 공유가 가능합니다.

📌 NavBackStackEntry란 무엇일까요?

NavBackStackEntry는 Jetpack Compose Navigation에서 탐색 스택의 항목을 나타내는 객체입니다. 각 화면으로 이동할 때마다, 해당 화면에 대한 NavBackStackEntry가 탐색 스택에 추가됩니다. NavBackStackEntry는 다음과 같은 정보를 제공합니다.

  • destination: 이동한 화면의 목적지(NavDestination)
  • arguments: 화면으로 전달된 인자(Bundle)
  • lifecycle: 화면의 생명주기(Lifecycle)
  • viewModelStore: 화면과 관련된 ViewModelStore

📌 NavBackStackEntry를 활용한 ViewModel 공유 원리

NavBackStackEntry를 활용한 ViewModel 공유의 핵심은 NavGraph의 ViewModelStoreOwner를 이용하는 것입니다.

  • NavGraph의 ViewModelStoreOwner: NavHost의 NavGraph는 자체적인 ViewModelStoreOwner를 가지고 있습니다. 이 ViewModelStoreOwner는 NavGraph 내에서 생성된 ViewModel 인스턴스를 관리합니다.
  • NavBackStackEntry를 통한 접근: NavGraph 내의 각 composable은 NavBackStackEntry를 통해 NavGraph의 ViewModelStoreOwner에 접근할 수 있습니다.
  • hiltViewModel(parentEntry): hiltViewModel() 함수에 NavBackStackEntry를 전달하면, 해당 NavBackStackEntry가 속한 ViewModelStoreOwner를 기준으로 ViewModel을 찾거나 생성합니다.
  • 공유: NavGraph내에서 hiltViewModel(parentEntry)를 사용하면, 동일한 ViewModelStoreOwner를 사용하기 때문에, 동일한 ViewModel 인스턴스를 공유할 수 있습니다.

📌 NavBackStackEntry를 활용한 ViewModel 공유 방법

다음은 NavBackStackEntry를 활용하여 ViewModel을 공유하는 구체적인 방법입니다.

  1. NavHost 설정: NavHost를 사용하여 화면 간의 탐색을 정의합니다.
  2. composable 설정: 각 화면에 대한 composable을 설정합니다.
  3. NavBackStackEntry 가져오기: 각 composable에서 navController.currentBackStackEntry를 사용하여 현재 NavBackStackEntry를 가져옵니다.
  4. parentEntry 가져오기: remember를 사용하여 navController.getBackStackEntry("route")를 사용하여 공유할 ViewModel의 NavBackStackEntry를 가져옵니다.
  5. hiltViewModel(parentEntry): hiltViewModel() 함수에 parentEntry를 전달하여 ViewModel을 생성하거나 가져옵니다.
  6. ViewModel 전달: 생성한 ViewModel을 각 화면에 전달합니다.

📌 예시 코드

val navController = rememberNavController()

    NavHost(navController = navController, startDestination = Screen.TimerScreen.route) {
		composable(Screen.TimerScreen.route) { entry ->
			val parentEntry = remember(entry) {
                navController.getBackStackEntry(Screen.TimerScreen.route)
            }
            val timerViewModel: TimerViewModel = hiltViewModel(parentEntry)
			... 
		}
		composable(Screen.OneMinuteScreen.route) { entry ->
            val parentEntry = remember(entry) {
                navController.getBackStackEntry(Screen.TimerScreen.route)
            }
            val timerViewModel: TimerViewModel = hiltViewModel(parentEntry)
            ... 
        }
        composable("Screen.MoreScreen.route) { entry ->
            val parentEntry = remember(entry) {
                navController.getBackStackEntry(Screen.TimerScreen.route)
            }
            val timerViewModel: TimerViewModel = hiltViewModel(parentEntry)
            ... 
        }
    }
}

0개의 댓글