탐색 및 백 스택

NavController에는 사용자가 방문한 대상이 포함된 '백 스택'이 있습니다. 사용자가 앱 전체에서 화면 간에 이동할 때 NavController는 대상을 백 스택에 추가하거나 백 스택에서 삭제합니다.

백 스택은 '후입선출' 데이터 구조인 스택입니다. 따라서 NavController는 항목을 스택 상단으로 푸시하고 스택 상단에서 항목을 팝합니다.

기본 동작

다음은 백 스택의 동작과 관련하여 고려해야 하는 핵심 사실입니다.

  • 첫 번째 대상: 사용자가 앱을 열면 NavController는 첫 번째 대상을 백 스택의 맨 위로 푸시합니다.
  • 스택으로 푸시: 각 호출 NavController.navigate()는 지정된 대상을 스택 상단으로 푸시합니다.
  • 최상단 대상 팝하기: 위로 또는 뒤로를 탭하면 NavController.navigateUp()NavController.popBackStack() 메서드가 각각 호출되어 스택에서 최상단 대상을 팝합니다. 위로뒤로의 차이점에 관한 자세한 내용은 탐색 원리 페이지를 참고하세요.

돌아가기

NavController.popBackStack() 메서드는 현재 대상을 백 스택에서 팝하고 이전 대상으로 이동하려고 시도합니다. 이렇게 하면 탐색 기록에서 사용자가 한 단계 뒤로 효과적으로 이동할 수 있습니다. 대상으로 다시 돌아가는지 여부를 나타내는 불리언 값을 반환합니다.

특정 대상으로 돌아가기

popBackStack()을 사용하여 특정 대상으로 이동할 수도 있습니다. 이렇게 하려면 오버로드 중 하나를 사용하세요. 정수 id 또는 문자열 route와 같이 식별자를 전달할 수 있는 몇 가지가 있습니다. 이러한 오버로드는 사용자를 지정된 식별자와 연결된 대상으로 이동합니다. 결정적으로, 스택에서 해당 대상 위의 모든 항목을 팝합니다.

이러한 오버로드는 inclusive 불리언도 사용합니다. NavController가 지정된 대상으로 이동한 후에 이를 백 스택에서 팝해야 하는지도 결정합니다.

예를 들어 다음 간단한 스니펫을 살펴보세요.

navController.popBackStack(R.id.destinationId, true)

여기서 NavController는 정수 ID가 destinationId인 대상으로 다시 돌아갑니다. inclusive 인수 값이 true이므로 NavController는 지정된 대상을 백 스택에서 팝합니다.

실패한 돌아가기 처리

popBackStack()false를 반환하면 이후 NavController.getCurrentDestination() 호출에서 null이 반환됩니다. 즉, 앱이 백 스택에서 마지막 대상을 팝했습니다. 이 경우 사용자에게는 빈 화면만 표시됩니다.

이는 다음과 같은 경우에 발생할 수 있습니다.

  • popBackStack()이 스택에서 아무것도 팝하지 않았습니다.
  • popBackStack()이 백 스택에서 대상을 팝했으며 이제 스택이 비어 있습니다.

이 문제를 해결하려면 새 대상으로 이동하거나, 활동에서 finish()를 호출하여 종료해야 합니다. 다음 스니펫은 이 작업을 보여줍니다.

Kotlin

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

Java

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

대상으로 팝업

한 대상에서 다른 대상으로 이동할 때 백 스택에서 대상을 삭제하려면 연결된 navigate() 함수 호출에 popUpTo() 인수를 추가하세요. popUpTo()navigate() 호출의 일부로 백 스택에서 일부 대상을 삭제하도록 탐색 라이브러리에 지시합니다. 매개변수 값은 백 스택에 있는 대상의 식별자입니다. 식별자는 정수 id 또는 문자열 route일 수 있습니다.

inclusive 매개변수의 인수를 true 값과 함께 포함하여 popUpTo()에서 지정한 대상도 백 스택에서 팝해야 함을 나타낼 수 있습니다.

이를 프로그래매틱 방식으로 구현하려면 inclusivetrue로 설정하여 NavOptions의 일부로 popUpTo()navigate()에 전달합니다. 이는 Compose와 뷰 모두에서 작동합니다.

팝업 시 상태 저장

popUpTo를 사용하여 대상으로 이동할 때 백 스택에서 팝된 모든 대상의 상태를 선택적으로 저장할 수 있습니다.

이 옵션을 사용 설정하려면 연결된 action에서 popUpToSaveStatetrue로 정의하거나 NavController.navigate()를 호출하세요.

대상으로 이동할 때 restoreSaveStatetrue로 정의하여 destination 속성의 대상과 연결된 상태를 자동으로 복원할 수도 있습니다.

XML 예

다음은 작업을 사용하는 XML의 popUpTo 예입니다.

<action
  android:id="@+id/action_a_to_b"
  app:destination="@id/b"
  app:popUpTo="@+id/a"
  app:popUpToInclusive="true"
  app:restoreState=”true”
  app:popUpToSaveState="true"/>

Compose 예

다음은 Compose에서 이와 동일한 예입니다.

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: String = "destination_a"
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable("destination_a") {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to the "destination_a" destination off the back stack before
                // navigating to the "destination_b" destination
                    navController.navigate("destination_b") {
                        popUpTo("destination_a") {
                            inclusive = true
                            saveState = true
                        }
                    }
                },
            )
        }
        composable("destination_b") { DestinationB(/* ... */) }
    }
}

@ Composable
fun DestinationA(onNavigateToB: () -> Unit) {
    Button(onClick = onNavigateToB) {
        Text("Go to A")
    }
}

보다 세밀하게는 다음과 같이 NavController.navigate()를 호출하는 방법을 변경할 수 있습니다.

// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a")
}

// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a") { inclusive = true }
}

// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
    launchSingleTop = true
}

NavController.navigate()에 옵션을 전달하는 방법에 관한 일반적인 정보는 옵션으로 탐색 가이드를 참고하세요.

작업을 사용한 팝

작업을 사용하여 이동할 때 백 스택에서 추가 대상을 팝하도록 선택할 수 있습니다. 예를 들어, 앱에 초기 로그인 흐름이 있다면 사용자가 로그인했을 때 사용자가 뒤로 버튼을 눌러도 로그인 흐름으로 돌아가지 않도록 로그인과 관련된 모든 대상을 백 스택에서 팝해야 합니다.

추가 자료

자세한 내용은 다음 페이지를 참고하세요.

  • 순환 탐색: 탐색 흐름이 순환인 경우 과도하게 채워진 백 스택을 방지하는 방법을 알아봅니다.
  • 대화상자 대상: 대화상자 대상이 백 스택을 관리하는 방법에 고유한 고려사항을 도입하는 방법을 알아봅니다.