์—ฌ์ฐจ์ €์ฐจ ํ•˜๋‹ค๋ณด๋‹ˆ ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ์˜ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ถ€๋ถ„์„ ๋‚ ๋ ค๋ฒ„๋ ธ๋‹ค๐Ÿฅฒ

๋‹ค์‹œํ•˜๋ ค๊ณ  ํ•˜๋Š”๋ฐ ์ฝ”๋“œ๋ฅผ ์žŠ์–ด๋ฒ„๋ ค์„œ ๋‚˜์ค‘์— ๋˜ ์ด๋Ÿด๊นŒ๋ด ๋ฏธ๋ฆฌ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ข€ ์ •๋ฆฌํ–ˆ๋‹ค.

์‚ฌ์‹ค ๊ท€์ฐฎ์€ ์ž‘์—…์ผ ์ค„ ์•Œ์•˜๋Š”๋ฐ ๊ทธ๋™์•ˆ ์ƒˆ๋กœ ์—…๋ฐ์ดํŠธ๋œ ๋ถ€๋ถ„์„ ์ ‘ํ•ด๋ณด๊ฒŒ ๋˜์–ด์„œ ๋‚˜๋ฆ„ ๋ณด๋žŒ์žˆ์—ˆ๋‹ค.

compose navigation์€ ์™œ ํ•„์š”ํ•œ๊ฐ€?

์‚ฌ์‹ค ์ฒ˜์Œ์—๋Š” ์ปดํฌ์ฆˆ์— ์ด๋Ÿฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ตณ์ด ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์žˆ์„๊นŒ ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

์กฐ๊ฑด๋ฌธ์œผ๋กœ UI๋ฅผ ์ œ์–ดํ•˜๋ฉด ํ™”๋ฉด ์ „ํ™˜์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ทธ๋ ‡์ง€๋งŒ ํ™”๋ฉด์„ ์ œ์–ดํ•œ๋‹ค๋Š” ๊ฒƒ์€ ํ™”๋ฉด์˜ ์ด๋™ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ทธ ์ด๋™ ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ, ๋ฐฑ์Šคํƒ ๊ด€๋ฆฌ ๋“ฑ ์ƒ๊ฐ๋ณด๋‹ค ๊ณ ๋ คํ•ด์•ผํ•  ์‚ฌํ•ญ์ด ๋งŽ์•˜๊ณ , compose navigation๋ฅผ ํ†ตํ•ด ์ด๋Ÿฌํ•œ ๋ถ€๋ถ„์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์žฅ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

์šฉ์–ด

  • Controller : destination๊ฐ„ ์ด๋™์„ ์ œ์–ดํ•˜๋Š” central coordinator.
  • Host : ํ˜„์žฌ ํ™”๋ฉด์„ ํฌํ•จํ•˜๋Š” UI์š”์†Œ, ๋Œ€์ถฉ ์•ก์ž ๊ฐ™์€๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ํŽธํ•จ. ๋‚ด๋ถ€์— ํ™”๋ฉด์„ ์Š‰์Š‰๋ฐ”๊ฟ”์„œ ์›ํ•˜๋Š” destination์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด ์•ก์ž๊ฐ™์€ ๋Š๋‚Œ.
  • Graph : ์•ฑ ๋‚ด์˜ ๋ชจ๋“  navigation destination๊ณผ ํ™”๋ฉด ์—ฐ๊ฒฐ ๋ฐฉ์‹์„ ์ •์˜ํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ. ์—ฌ๊ธฐ์— ๋ฏธ๋ฆฌ ์ •์˜๋œ ํ™”๋ฉด์œผ๋กœ๋งŒ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋Œ€์ถฉ ์ง€๋„ ๋Š๋‚Œ.
  • Destination : navigation graph์˜ ๋…ธ๋“œ. host๊ฐ€ ๋ณด์—ฌ์ฃผ๋Š” ํ™”๋ฉด
  • Route : destination์œผ๋กœ ์ด๋™ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ๋กœ. destination๊ณผ ์ด๋™ ์‹œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ Serializable๊ฐ์ฒด(ex ๋ฌธ์ž์—ด)

๊ฐœ์ธ์ ์œผ๋กœ๋Š” ๊ฐœ๋… ์ž์ฒด๋Š” ์•ฝ๊ฐ„ ์ด๋Ÿฐ ๋Š๋‚Œ์ด๋‹ค.

dependency

compose navigation์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ชจ๋“ˆ ๋ ˆ๋ฒจ์˜ build.gradle ํŒŒ์ผ์— ์•„๋ž˜์™€ ๊ฐ™์€ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์–ด์•ผํ•œ๋‹ค.

2.8.0-alpha08 ์ด ๋ฒ„์ „์„ ๋ถ„๊ธฐ๋กœ ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ๊ฐ€์ง„ route์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์—ˆ๋‹ค.

ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ์œ„ํ•œ route ์—…๋ฐ์ดํŠธ๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด ํ•ด๋‹น ๋ฒ„์ „ ์ด์ƒ์œผ๋กœ ๋ฒ„์ „์„ ์„ค์ •ํ•ด์•ผํ•œ๋‹ค.

dependencies{
    val nav_version="2.8.0-alpha08"
    implementation ("androidx.navigation:navigation-compose:$nav_version")
}

๊ฐ„๋‹จํ•œ ์‚ฌ์šฉ๋ฒ•

1. navigation Controller์ƒ์„ฑ

val navController=rememverNavController()
  • ์ปดํฌ์ €๋ธ” ๊ณ„์ธต๊ตฌ์กฐ์—์„œ ์ฐธ์กฐํ•  ๋ชจ๋“  ์ปดํฌ์ €๋ธ”์„ ์ฐธ์กฐ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ„์ธต์—์„œ ์ƒ์„ฑํ•ด์•ผํ•œ๋‹ค. (state hoisting)

2. navigation Host์ƒ์„ฑ

๋ฐฉ๋ฒ•1) route๋ฅผ ํƒ€์ž…์œผ๋กœ ์ „๋‹ฌํ•˜๊ธฐ (*Navigation 2.8.0-alpha08ย ์ด์ƒ์—์„œ๋งŒ ๊ฐ€๋Šฅ)

  • ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ณด๋‚ด๋Š”๋ฐ ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ๋”ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
//@Serializable์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ์„ ์œ„ํ•œ ์˜์กด์„ฑ ์ถ”๊ฐ€
//build.gradle(module)
plugins {
    kotlin("plugin.serialization") version "2.0.0" // ์‚ฌ์šฉ์ค‘์ธ ์ฝ”ํ‹€๋ฆฐ ๋ฒ„์ „์— ๋งž์ถฐ์•ผ ์˜ค๋ฅ˜๊ฐ€ ์•ˆ๋‚˜๋Š”๊ฒƒ ๊ฐ™๋‹ค.
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
}

@Serializable
object Profile
@Serializable
object FriendsList

NavHost(navController=navController,startDestination=ScreenA){
//์š”๊ธฐ๊ฐ€ NavGraphBuilder์˜์—ญ์œผ๋กœ, ์—ฌ๊ธฐ ์ฝ”๋“œ๋กœ NavGraph๊ฐ€ ์ •์˜๋˜์–ด NavHost์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค.
	composable<ScreenA>{ScreenA(/*...*/)}
	composable<ScreenB>{ScreenB(/*...*/)}
}

๋ฐฉ๋ฒ•2) route์— string์ด๋‚˜ id์ „๋‹ฌ

  • NavHost์—์„œ NavGraph๋ฅผ ๋งŒ๋“ค๋•Œ route์— string์ด๋‚˜ ์ •์ˆ˜ id๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿšจย ์ด ๊ฒฝ์šฐ ๋‚˜์ค‘์— ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ณด๋‚ผ ๋•Œ โ€œscreenA/arg1/arg2โ€ ๋ญ ์ด๋Ÿฐ ์‹์œผ๋กœ route๋ฅผ ์ „๋‹ฌํ•˜๊ฒŒ ๋˜์„œ Type safetyํ•˜์ง€ ์•Š๋‹ค.

NavHost(navController=navController,startDestination="ScreenA"){
//๊ฐœ์ธ์ ์œผ๋กœ ์ด๋ถ€๋ถ„์€ ๋‹ค๋ฅธ ํŒŒ์ผ์ด๋‚˜ ํ•˜์—ฌ๊ฐ„ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•ด๋„ ์ข‹์„๊ฒƒ๊ฐ™๋‹ค.
	composable(route="ScreenA"){ScreenA(/*...*/)}
	composable(route="ScreenB"){ScreenB(/*...*/)}
}
graph:NavGraphBuilder.()->Unit={
	composable(route="ScreenA"){ScreenA(/*...*/)}
	composable(route="ScreenB"){ScreenB(/*...*/)}
}

NavHost(navController=navController,startDestination=ScreenA,builder=graph)
//ํ™”๋ฉด์ด ๋งŽ์œผ๋ฉด graph๋ถ€๋ถ„์„ ๋‹ค๋ฅธ ํŒŒ์ผ๋กœ ๋นผ๋Š”๊ฒŒ ๋„์›€์ง€ ๋  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ?

โšก๏ธ NavHost

@Composable
public fun NavHost(
    navController: NavHostController,
    startDestination: String,
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.Center,
    route: String? = null,
    enterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) =
        { fadeIn(animationSpec = tween(700)) },
    exitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) =
        { fadeOut(animationSpec = tween(700)) },
    popEnterTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition) =
        enterTransition,
    popExitTransition: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition) =
        exitTransition,
    builder: NavGraphBuilder.() -> Unit
)

โšก๏ธ NavGraph

public fun NavGraphBuilder.composable(
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    enterTransition: (@JvmSuppressWildcards
        AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)? = null,
    exitTransition: (@JvmSuppressWildcards
        AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition?)? = null,
    popEnterTransition: (@JvmSuppressWildcards
        AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)? =
            enterTransition,
    popExitTransition: (@JvmSuppressWildcards
        AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition?)? =
            exitTransition,
    content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit
)

3. destination์œผ๋กœ ์ด๋™

navController๋ฅผ ํ™œ์šฉํ•˜์—ฌ destination์œผ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์–‘ํ•˜๊ฒŒ overload ๋˜์–ด ์žˆ์–ด ์ƒํ™ฉ์— ๋งž์ถฐ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@MainThread
public void navigate(@NonNull Uri deepLink)

@MainThread
open fun navigate(resId: @IdRes Int): Unit

@MainThread
open fun navigate(directions: NavDirections): Unit

//.. ๋“ฑ๋“ฑ

  • Composable๋กœ ์ด๋™ ์‹œ
@Serializable
object FriendsList

navController.navigate(route = FriendsList)

//or route๋ฅผ string์œผ๋กœ ์‚ฌ์šฉ์‹œ

navController.navigate("FriendsList")

๐Ÿšจย ์ฃผ์˜ํ• ์ ์€ ๋งŒ์•ฝ ์ปดํฌ์ €๋ธ” ๋‚ด๋ถ€์—์„œ ํ™”๋ฉด์ด๋™์„ ์ œ์–ดํ•˜๊ณ  ์‹ถ์„ ์‹œ ์ž์‹ ์ปดํฌ์ €๋ธ”๋กœ navController๋ฅผ ๋ณด๋‚ด์„œ ์ž์‹ ์ปดํฌ์ €๋ธ” ๋‚ด๋ถ€์—์„œ navigate๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ๋œ๋‹ค. UDF(๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐํ”Œ๋กœ์šฐ)์›์น™์— ๋”ฐ๋ผ ์ด๋ฒคํŠธ๋ฅผ ๋…ธ์ถœํ•ด์„œ ์ƒ์œ„์—์„œ NavController๋ฅผ ์ œ์–ดํ•ด์•ผํ•œ๋‹ค.

@Composable
fun Parent(){
	val navController=rememberNavController()
	//...
	Child(){
		navController.navigate(/*destination route*/)
	}
}

@Composable
fun Child(onScreenChange:()->Unit){
}

์ฐธ๊ณ 

Navigation with Compose ย |ย  Jetpack Compose ย |ย  Android Developers

Navigation ย |ย  Android Developers

Kotlinx-Serialization | ๋งค์‰ฌ์—… ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์ž

Serialization | Kotlin

GitHub - Kotlin/kotlinx.serialization: Kotlin multiplatform / multi-format serialization

NavController ย |ย  Android Developers

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ