내비게이션 그래프 설계 개요

kosdjs·2025년 12월 19일

Android

목록 보기
29/29
  • 내비게이션 컴포넌트는 앱의 탐색을 관리하기 위해 내비게이션 그래프를 사용함, 내비게이션 그래프는 앱의 모든 목적지와 그들이 어떻게 연결되어 있는지를 가지고 있는 자료구조임

노트: 내비게이션 그래프는 사용자가 최근에 방문한 목적지를 보관하는 스택인 백스택과는 별개입니다.

목적지 유형

  • 호스팅된 목적지: 내비게이션 호스트 전체를 채움, 즉, 호스팅된 목적지가 내비게이션 호스트와 같은 사이즈며 이전 목적지는 보이지 않게 됨, 메인 및 상세 화면의 경우에 사용됨

  • 대화상자 목적지: 오버레이 UI 컴포넌트를 제공함, 이 UI는 내비게이션 호스트의 크기나 위치에 묶이지 않으며 이전 목적지가 이 목적지 아래에 보이게 됨, 알림, 선택, 양식 창을 띄우는 경우에 사용됨

  • 액티비티 목적지: 앱 내의 고유의 화면이나 기능을 나타냄, 내비게이션 그래프의 종료 지점과 같이 취급되며 내비게이션 컴포넌트와 독립적으로 관리되는 새로운 액티비티를 시작함, 최근 안드로이드 개발에서는 앱이 단일 액티비티로 이루어지므로 다른 앱의 액티비티나 마이그레이션 과정의 일부로 사용하기 적합함

  • 이 문서에서는 호스팅된 목적지의 예시를 제공며 이는 가장 기본적이고 많이 쓰이는 목적지임, 대화상자 목적지나, 액티비티 목적지는 별도의 문서가 있음

프레임워크

  • Compose: NavHost 컴포저블을 사용함, 코틀린 DSL을 사용해 NavGraph 를 추가함, 그래프를 다음 두가지 방법 중 하나로 생성 가능함

    • NavHost의 일부: NavHost를 생성할 때 NavHost의 일부로 직접 생성하는 방법
    • 프로그래밍: NavController.createGraph() 메소드를 사용해 NavGraph 를 생성하고 NavHost 에 전달하는 방법
  • 프래그먼트: 뷰 UI 프레임워크를 사용한 프래그먼트를 사용한다면 NavHostFragment 를 호스트로 사용함, 다음과 같은 방법들로 내비게이션 그래프를 생성함

    • 프로그래밍: 코틀린 DSL을 사용해 NavGraph 를 생성하고 직접 NavHostFragment 에 적용함
      • 코틀린 DSL의 createGraph() 함수가 사용되는 것은 프래그먼트와 Compose에서 동일함
    • XML: XML에 직접 내비게이션 호스트와 그래프를 작성
    • 안드로이드 스튜디오 편집기: 안드로이드 스튜디오의 GUI 편집기를 사용해 그래프를 XML 리소스 파일로 생성하고 수정할 수 있음

노트: NavController를 통해 그래프와 상호착용을 하는 방법은 프레임워크간에 비슷합니다. 세부 정보는 목적지로 탐색하기를 확인하십시오.

Compose

  • Compose에서 경로를 정의하기 위해 직렬화 가능한 객체나 클래스를 사용함, 경로는 목적지로 도달하는 방법을 묘사하며 목적지가 필요로하는 모든 정보를 포함함

  • @Serializable 어노테이션을 사용하면 경로 유형을 위해 필요한 직렬화, 역직렬화 메소드를 자동으로 생성해줌, 이 주석은 코틀린 직렬화 플러그인에서 제공함, 코틀린 직렬화 플러그인 추가를 따라서 플러그인을 추가할 수 있음

NavHost 컴포저블을 사용해 내비게이션 그래프를 생성하는 예시

@Serializable
object Profile
@Serializable
object FriendsList

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
    // 위와 비슷하게 목적지를 추가할 수 있습니다.
}
  1. 직렬화 가능한 객체는 각각 두 경로 ProfileFriendList를 나타냄

  2. NavHost 컴포저블을 호출할 때 NavController 와 시작 목적지의 경로를 전달함

  3. NavHost 에 전달되는 람다 함수는 궁극적으로 NavController.createGraph() 를 호출하고 NavGraph 를 반환함

  4. 각 경로는 NavGraphBuilder.composable<T>() 에 유형 인자로 전달되며 이는 결과적으로 생성되는 NavGraph 에 목적지를 추가함

  5. 람다에 전달되는 composableNavHost 가 그 목적지에서 표시해야 하는 것

주의: composable() 에 유형 대신 경로의 문자열 또는 정수 id를 전달할 수 있습니다. 하지만 이는 목적지로 전달하는 추가 인자를 관리하기 힘들게 만듭니다.

람다 이해하기

  • NavGraph 를 생성하는 람다를 더 잘 이해하려면 다음 코드와 같이 NavController.createGraph() 를 독립적으로 사용해 NavGraph 를 만들고 NavHost 에 직접 전달할 수 있다는 점을 생각하면 됨
val navGraph by remember(navController) {
  navController.createGraph(startDestination = Profile)) {
    composable<Profile> { ProfileScreen( /* ... */ ) }
    composable<FriendsList> { FriendsListScreen( /* ... */ ) }
  }
}
NavHost(navController, navGraph)

중요: NavController 는 단일 NavHost 컴포저블과 연결되어 있습니다. NavHostNavController 에 자신의 내비게이션 그래프에 대한 접근을 제공합니다. NavController 를 사용해 목적지로 탐색을 하도록 하면 NavController 가 연결된 NavHost 와 상호작용하도록 만드는 것입니다.

인자 전달하기

  • 목적지로 데이터를 전달하고 싶다면 경로를 인자를 가지는 클래스로 정의하면 됨, 다음은 name 인자를 가지는 Profile 경로의 데이터 클래스의 예시임
@Serializable
data class Profile(val name: String)
  • 이렇게 만든 목적지로 인자를 전달해야 할 때마다 경로 클래스 생성자에 인자를 전달해 인스턴스를 생성하면 됨

노트: 인자가 필요한 경로에 data class 를 사용하고, 인자가 필요하지 않은 경로에는 objectdata object 를 사용하십시오.

  • 선택적(optional) 인자를 위해서는 다음 예시와 같이 기본 값을 가지며 null을 가질 수 있는 필드로 만들면 됨
@Serializable
data class Profile(val nickname: String? = null)

경로 인스턴스 얻기

  • NavBackStackEntry.toRoute() 또는 SavedStateHandle.toRoute() 를 이용해 경로 인스턴스를 얻을 수 있음, composable() 을 사용해 목적지를 생성하면 NavBackStackEntry 는 다음 예시처럼 파라미터로 사용이 가능함
@Serializable
data class Profile(val name: String)

val navController = rememberNavController()

NavHost(navController = navController, startDestination = Profile(name="John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(name = profile.name) }
}
  • Profile 경로는 내비게이션 그래프의 시작 목적지이며 name 인수에 "John Smith" 값을 가지고 있음

  • composable<Profile>() 블록이 목적지임

  • ProfileScreen 컴포저블은 profile.name 값을 자신의 name 인자 값으로 가져감, 이로 인해 "John Smith" 값이 ProfileScreen 으로 전달됨

전체 예시

  • 다음 예시는 NavControllerNavHost 를 같이 사용하는 예시임
@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// ProfileScreen 컴포저블을 정의합니다.
@Composable
fun ProfileScreen(
    profile: Profile
    onNavigateToFriendsList: () -> Unit,
  ) {
  Text("Profile for ${profile.name}")
  Button(onClick = { onNavigateToFriendsList() }) {
    Text("Go to Friends List")
  }
}

// FriendsListScreen 컴포저블을 정의합니다.
@Composable
fun FriendsListScreen(onNavigateToProfile: () -> Unit) {
  Text("Friends List")
  Button(onClick = { onNavigateToProfile() }) {
    Text("Go to Profile")
  }
}

// `NavController` 와 `NavHost` 를 포함하는 MyApp 컴포저블을 정의합니다.
@Composable
fun MyApp() {
  val navController = rememberNavController()
  NavHost(navController, startDestination = Profile(name = "John Smith")) {
    composable<Profile> { backStackEntry ->
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(
            profile = profile,
            onNavigateToFriendsList = {
                navController.navigate(route = FriendsList)
            }
        )
    }
    composable<FriendsList> {
      FriendsListScreen(
        onNavigateToProfile = {
          navController.navigate(
            route = Profile(name = "Aisha Devi")
          )
        }
      )
    }
  }
}
  • 컴포저블에 NavController 를 직접 전달하는 대신 NavHost 에 이벤트를 노출함, 컴포저블이 () -> Unit 유형의 인자를 가지도록 해 NavHostNavController.navigate() 를 호출하는 람다를 전달하도록 함

프래그먼트

  • 앞 절에서 설명했듯이 프래그먼트를 사용할 때 내비게이션 그래프를 생성하는 방법은 Kotlin DSL, XML, 또는 안드로이드 스튜디오를 사용하는 방법이 있음

노트: 내비게이션 컴포넌트는 다수의 프래그먼트 목적지를 가지는 단일의 메인 액티비티를 가지는 앱을 위해 설계되었습니다. 메인 액티비티는 필요에 따라 목적지를 전환하는 역할을 하는 NavHostFragment 를 포함하는 내비게이션 그래프와 연결되어 있습니다. 다수의 액티비티 목적지를 가지는 앱은 각 액티비티가 자신의 내비게이션 그래프를 가지고 있습니다.

프로그래밍적 방법

  • 코틀린 DSL은 프래그먼트를 사용할 때 내비게이션 그래프를 생성하는 프로그래밍적 방법을 제공함, 이 방식은 XML 리소스 파일을 사용하는 것보다 더 깔끔하고 현대적인 방법임

다음은 두 화면이 있는 내비게이션 그래프를 구현하는 예제임

  • 먼저 필수적으로 NavHostFragment 를 생성해야 함, 이는 app:navGraph 요소에 포함되지 않아야 함
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>
  • 다음으로 NavHostFragmentidNavController.findNavController() 에 전달해야함, 이는 NavHostFragment 와 NavController 를 연결하는 작업임

  • 그 후에 NavController.creageGraph() 를 호출해 그래프를 NavController 에 연결하고 이는 결과적으로 NavHostFragment 에 연결하는 것이 됨

@Serializable
data class Profile(val name: String)

@Serializable
object FriendsList

// NavController를 전달받음.
val navController = findNavController(R.id.nav_host_fragment)

// `createGraph()`로 NavController에 그래프를 추가한다.
navController.graph = navController.createGraph(
    startDestination = Profile(name = "John Smith")
) {
    // 각 목적지를 경로 상수 중 하나와 연결한다.
    fragment<ProfileFragment, Profile> {
        label = "Profile"
    }

    fragment<FriendsListFragment, FriendsList>() {
        label = "Friends List"
    }

    // 다른 프래그먼트 목적지도 비슷하게 추가한다.
}
  • DSL을 사용하는 이 방법은 이전 부분인 Compose 에서 사용하는 방법과 비슷한 방식임, 예시로 둘 다 NavGraph 를 생성하기 위해 NavController.createGraph() 함수를 사용함, 마찬가지로 Compose 에서 NavGraphBuilder.composable() 가 그래프에 컴포저블 목적지를 추가하는 것처럼, 여기서는 NavGraphBuilder.fragment() 를 사용해 프래그먼트 목적지를 추가함

XML

  • 직접 XML을 작성할 수도 있음, 다음 예시는 이전 부분에서 다루었던 두 화면을 가지는 그래프 예시와 동일한 역할을 하는 예시임

  • 먼저 NavHostFragment 를 생성함, 이는 실제 내비게이션 그래프를 포함하는 내비게이션 호스트 역할을 함

NavHostFragment 의 최소 구현 예시

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/nav_graph" />

</FrameLayout>
  • NavHostFragmentapp:navGraph 속성을 포함함, 이 속성을 사용해 내비게이션 그래프를 연결할 수 있음, 다음 예제는 그래프를 구현하는 방법을 보여줌
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/profile">

    <fragment
        android:id="@+id/profile"
        android:name="com.example.ProfileFragment"
        android:label="Profile">

        <!-- Profile 에서 Friends List로 탐색하는 액션입니다. -->
        <action
            android:id="@+id/action_profile_to_friendslist"
            app:destination="@id/friendslist" />
    </fragment>

    <fragment
        android:id="@+id/friendslist"
        android:name="com.example.FriendsListFragment"
        android:label="Friends List" />

    <!-- 다른 프래그먼트 목적지를 비슷하게 추가할 수 있습니다. -->
</navigation>
  • 액션을 사용해 목적지간의 연결을 정의할 수 있음, 이 예제에서는 profile 프래그먼트가 friendslist 로 탐색하는 액션을 포함함

노트: DSL 예제에서는 이 개념이 적용되지 않기 때문에 액션을 적용하지 않습니다. DSL을 사용중이라면 NavController.navigate() 를 사용하세요.

원문: https://developer.android.com/guide/navigation/design

0개의 댓글