๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Android

[Android] Jetpack Compose - Navigation์˜ Destination ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ (NavBackStackEntry.

interfacer_han 2024. 9. 13. 13:54

#1 ์ด์ „ ๊ธ€

[Android] Jetpack Compose - Navigation ๊ธฐ์ดˆ

#1 ๊ฐœ์š”#1-1 ์ „ํ†ต์ ์ธ ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ Navigation [Android] Navigation - ๊ธฐ์ดˆ#1 ์ด์ „ ๊ธ€ [Android] Navigation - ํ™˜๊ฒฝ ์„ค์ • #1 Navigation#1-1 ์•กํ‹ฐ๋น„ํ‹ฐ ๋ฐ ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ตฌ์„ฑ์˜ ํŠธ๋ Œ๋“œ์š”์ฆ˜ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ

kenel.tistory.com

๋ณธ ๊ฒŒ์‹œ๊ธ€์—์„  ์œ„ ๊ฒŒ์‹œ๊ธ€์˜ ์™„์„ฑ๋œ ์•ฑ์„ ์ผ๋ถ€ ์ˆ˜์ •ํ•˜์—ฌ, Destination๋ผ๋ฆฌ ์„œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ฒŒ ๋งŒ๋“ค์–ด๋ณธ๋‹ค.
 
์ด๋ฅผ ์œ„ํ•ด์„œ ๋จผ์ € NavBackStackEntry์— ๋Œ€ํ•ด ์•Œ์•„์•ผ ํ•œ๋‹ค. ์ด๋ฆ„์—์„œ ๋ณด๋“ฏ, NavBackStackEntry์€ ๋ฐฑ ์Šคํƒ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด๋‹ค. ๋”ฐ๋ผ์„œ, Back Stack(๋ฐฑ ์Šคํƒ)์ด ๋ญ”์ง€ ์•Œ์•„์•ผ NavBackStackEntry๋ฅผ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.
 

#2 Back Stack

#2-1 ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋…

๋ฐฑ ์Šคํƒ์€ ๋งˆ์น˜ ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ์˜ ๋ฐฉ๋ฌธ ๊ธฐ๋ก๊ณผ ๊ฐ™๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. ์›น ์„œํ•‘์„ ํ•˜๋‹ค๊ฐ€ ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ฐ€์žฅ ์ตœ๊ทผ์— ๋ฐฉ๋ฌธํ•œ ํŽ˜์ด์ง€๋ถ€ํ„ฐ ๊ฐ€์žฅ ์˜ˆ์ „์— ๋ฐฉ๋ฌธํ–ˆ๋˜ ํŽ˜์ด์ง€ ์ˆœ์œผ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด, ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ ๊ตฌ์กฐ์—์„œ๋„ ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ฐ€์žฅ ์ตœ๊ทผ์— '๋ฐฉ๋ฌธ'ํ•œ ํ™”๋ฉด์—์„œ ๊ฐ€์žฅ ์˜ˆ์ „์— ๋ฐฉ๋ฌธํ–ˆ๋˜ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•œ๋‹ค (๊ณ„์† ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋‹ค๋ณด๋ฉด ๋” ์ด์ƒ ๋ฐฉ๋ฌธํ–ˆ๋˜ ํ™”๋ฉด์ด ๋‚จ์•„์žˆ์ง€ ์•Š๊ฒŒ๋ ํ…๋ฐ, ์—ฌ๊ธฐ์„œ ํ•œ๋ฒˆ ๋” ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์•ฑ์ด ์ข…๋ฃŒ๋œ๋‹ค).
 

#2-2 ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ ๊ฐœ๋…

์ด์ „ ๊ฒŒ์‹œ๊ธ€(#1)์˜ ์™„์„ฑ๋œ ์•ฑ์—์„œ "Go to Second Screen"๊ณผ "Go to First Screen" ๋ฒ„ํŠผ์„ ๋ณด์ด๋Š” ๋Œ€๋กœ ๊ณ„์† ๋ˆ„๋ฅด๋ฉด ์œ„์™€ ๊ฐ™์€ ๋ฐฑ ์Šคํƒ์ด ํ˜•์„ฑ๋œ๋‹ค. ๋งจ โ†˜์ชฝ์— ์žˆ๋Š” ํ™”๋ฉด์—์„œ ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์˜ค๋ž˜๋œ ์ชฝ(โ†–)์œผ๋กœ ๊ฑฐ์Šฌ๋Ÿฌ ์˜ฌ๋ผ๊ฐ„๋‹ค. ์—ฌ๊ธฐ์„œ ์งš์–ด์•ผํ•  ์ ์ด ์žˆ๋‹ค. ๋ฐ”๋กœ "๋ฐฑ ์Šคํƒ์—๋Š” Back์ด๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋‹ˆ, 'ํ˜„์žฌ ๋ณด์ด๋Š” ํ™”๋ฉด'์€ Back Stack์— ํฌํ•จ๋˜์ง€ ์•Š๋Š”๊ฑด๊ฐ€?"๋ผ๋Š” ์˜๋ฌธ์ด ๊ทธ๊ฒƒ์ด๋‹ค. ์•„๋‹ˆ๋‹ค, ์ ์–ด๋„ ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ Back Stack ๊ฐœ๋…์—์„  'ํ˜„์žฌ ๋ณด์ด๋Š” ํ™”๋ฉด' ๋˜ํ•œ Back Stack์— ํฌํ•จ๋œ๋‹ค. ์ฆ‰, ์œ„ ๋„์‹๋„์—์„œ ๋งจ โ†˜์ชฝ์— ์žˆ๋Š” ํ™”๋ฉด ๋˜ํ•œ ๋ฐฑ์Šคํƒ์— ํฌํ•จ๋œ ์ƒํƒœ๋ผ๋Š” ๊ฒƒ์ด๋‹ค.
 

#3 NavBackStackEntry - ๋ฐ์ดํ„ฐ ์ „๋‹ฌ

#3-1 ๊ฐœ๋…

์•ˆ๋“œ๋กœ์ด๋“œ Navigation ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋Š” Destination์˜ Back Stack ํ•˜๋‚˜ํ•˜๋‚˜์˜ ๊ตฌ์„ฑ ํ•ญ๋ชฉ(Entry)์„ ๋งํ•œ๋‹ค. ๊ฐ Destination์„ ์ˆ˜์‹ํ•˜๋Š” ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋ผ๊ณ ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฐ์ฒด๋Š” Navigation ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‹จ์—์„œ ์•Œ์•„์„œ ๋งŒ๋“ค๊ณ  ๊ด€๋ฆฌํ•ด์ค€๋‹ค. ๋”ฐ๋ผ์„œ #2-2์— ์žˆ๋Š” ๊ทธ๋ฆผ๊ณผ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ NavBackStackEntry๋Š” ์ด 6๊ฐœ๊ฐ€ ์•”์‹œ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ ธ ํ•ด๋‹น ๋ฐฑ์Šคํƒ์˜ ์š”์†Œ๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ๊นŒ์ง€ ๊ฐ™์ด ์‚ด์•„ ์žˆ์„ ๊ฒƒ์ด๋‹ค.
 

#3-2 Destination์˜ content

// in MyNavHost.kt

composable(
    route = "secondScreen",
) { backStackEntry ->
    // backStackEntry๋ฅผ ๊ฐ€์ง€๊ณ  ๋ฌด์–ธ๊ฐˆ ํ•˜๋Š” ์ฝ”๋“œ
}

์ด์ „ ๊ฒŒ์‹œ๊ธ€์˜ #2-4์—์„œ ๋ณด๋“ฏ, Destination์˜ content ์ธ์ˆ˜๋Š” ๊ทธ ๋ฐ์ดํ„ฐํ˜•์ด @Composable (NavBackStackEntry) -> Unit์ด๋‹ค. ๋‹ค๋ฅธ ์ผ๋ฐ˜์ ์ธ ์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜๋“ค์˜ content ์ธ์ˆ˜ ํƒ€์ž…์ด @Composable () -> Unit์ธ ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ ๋ง์ด๋‹ค. ์ด๋Š” ์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด Destination์˜ ํ•˜์œ„ ์ปดํฌ์ €๋ธ”(content)๊ฐ€ NavBackStackEntry๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ง์ด ๋œ๋‹ค.
 
#2-2์—์„œ Back Stack์€ ํ˜„์žฌ Destination๋„ ํฌํ•จํ•˜๋Š” ๊ฐœ๋…์ด๋ผ๊ณ  ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์œ„ ์ฝ”๋“œ์˜ backStackEntry๋Š” ํ˜„์žฌ Destination์—๊ฒŒ ์ „๋‹ฌ๋œ ์ •๋ณด๋ผ๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋ง์ž. "Back์ด๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ์žˆ์œผ๋‹ˆ ์ด์ „(Back) Destination์— ๋Œ€ํ•œ ์ •๋ณด์ธ๊ฐ€?" ๋ผ๊ณ  ๋ฌด์‹ฌ์ฝ” ์ƒ๊ฐํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค.
 

#3-3 NavBackStackEntry.arguments ๊ตฌ์กฐ ์ •์˜

NavBackStackEntry์˜ arguments ํ”„๋กœํผํ‹ฐ๋Š” ๋ฐฑ ์Šคํƒ์˜ ๊ฐ ์š”์†Œ๋งˆ๋‹ค ๊ฐœ๋ณ„์ ์œผ๋กœ ์ €์žฅ๋˜๋Š” ๋ฐ์ดํ„ฐ๋‹ค. #3-2์˜ Destination ์ฝ”๋“œ๊ฐ€ arguments๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด String ํƒ€์ž… ๋ฐ์ดํ„ฐ ํ•˜๋‚˜์™€ Int ํƒ€์ž… ๋ฐ์ดํ„ฐ ํ•˜๋‚˜์”ฉ์„ arguments์— ์ •์˜ํ•œ๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์€ ๋ชจ์–‘์ด ๋œ๋‹ค.
 

// in MyNavHost.kt

composable(
    // (2) route ์ˆ˜์ •
    route = "secondScreen/{sampleStringDataName}/{sampleIntDataName}",
    // (1) arguments ์ถ”๊ฐ€
    arguments = listOf(
        navArgument("sampleStringDataName") {
            type = NavType.StringType
        },
        navArgument("sampleIntDataName") {
            type = NavType.IntType
        }
    )
) { backStackEntry ->
    // backStackEntry๋ฅผ ๊ฐ€์ง€๊ณ  ๋ฌด์–ธ๊ฐˆ ํ•˜๋Š” ์ฝ”๋“œ
}

(1) arguments ์ถ”๊ฐ€
์—ฌ๊ธฐ์„œ, "์™œ ๊ตณ์ด type์„ NavType์ด๋ผ๋Š” ๋ณ„๋„์˜ ์ธ์Šคํ„ด์Šค๊นŒ์ง€ ์ด์šฉํ•ด์„œ ๋ช…์‹œํ•˜๋Š”๊ฐ€?"๋ผ๋Š” ์˜๋ฌธ์ด ๋“ค ์ˆ˜ ์žˆ๋Š”๋ฐ, Destination ๊ฐ„ ๋ฐ์ดํ„ฐ๋Š” String ํƒ€์ž…์œผ๋กœ๋งŒ ์ „๋‹ฌ๋˜๊ธฐ์—, ์•ˆ์ „ํ•œ ํƒ€์ž… ์บ์ŠคํŒ…์„ ๋ณด์žฅํ•ด ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ์ด๋ผ๊ณ  ํ•œ๋‹ค. ํƒ€์ž…์„ ๋ช…์‹œํ•จ์œผ๋กœ์จ, ํ†ต์ œ๊ฐ€ ์–ด๋ ค์šด ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ํ†ต์ œ ๊ฐ€๋Šฅํ•œ ์ปดํŒŒ์ผ ์—๋Ÿฌ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.
 
(2) route ์ˆ˜์ • 
arguments ์ธ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ฒƒ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ route ์ธ์ˆ˜์˜ ๋ฌธ์ž์—ด๋„ ์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ๋ฐ์ดํ„ฐ๋ฅผ route ๋ฌธ์ž์—ด ์†์— ๋‹ด์•„ ๋ณด๋‚ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค (#3-4 ์ฐธ์กฐ). route ์ž์ฒด์— ์ •๋ณด๋ฅผ ์‹ค์–ด ๋ณด๋‚ธ๋‹ค๋Š” ์ ์ด, ๋งˆ์น˜ ์›น ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ URL์— ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์–ด ๋ณด๋‚ด๋Š” GET ์š”์ฒญ๊ณผ ๋น„์Šทํ•˜๋‹ค.
 
๊ทธ๋ฆฌ๊ณ  ์œ„ ์ฝ”๋“œ๋Š” arguments ํ”„๋กœํผํ‹ฐ์— ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์€ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, arguments๋ฅผ ์ฃผ๊ณ  ๋ฐ›๊ธฐ ์œ„ํ•œ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•œ ๊ฒƒ์— ๋ถˆ๊ณผํ•˜๋‹ค. ์‹ค์ œ๋กœ Destination์˜ arguments์— ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์œผ๋ ค๋ฉด ์ถ”๊ฐ€ ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค.
 

#3-4 NavBackStackEntry์— ๋ฐ์ดํ„ฐ(arguments) ๋‹ด์•„ ๋ณด๋‚ด๊ธฐ

composable(
    route = "firstScreen", arguments = listOf()
) {
    FirstScreen(navigateToSecondScreen = { navController.navigate("secondScreen/HelloWorld/123") })
}

#3-3์—์„œ Second Screen Destination์˜ route ์ธ์ˆ˜ ๋ฌธ์ž์—ด ํ˜•์‹์„ ์ƒˆ๋กญ๊ฒŒ ์ž‘์„ฑํ–ˆ์—ˆ๋‹ค. NavHostController.navigate()์— ์ „๋‹ฌํ•  ๋ฌธ์ž์—ด ํ˜•์‹๋„ ์ด์— ๋งž์ถ˜๋‹ค. ์—ฌ๊ธฐ๊นŒ์ง€ํ•ด์•ผ ๋น„๋กœ์†Œ arguments์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ด๊ธด๋‹ค.
 

#3-5 NavBackStackEntry์—์„œ ๋ฐ์ดํ„ฐ(arguments) ๊ฐ€์ ธ์˜ค๊ธฐ

// in MyNavHost.kt

composable(
    route = "secondScreen/{sampleStringDataName}/{sampleIntDataName}",
    arguments = listOf(
        navArgument("sampleStringDataName") {
            type = NavType.StringType
        },
        navArgument("sampleIntDataName") {
            type = NavType.IntType
        }
    )
) { backStackEntry ->
    SecondScreen(navigateToFirstScreen = { navController.navigate("firstScreen") })
    Log.i("interfacer_han", "${backStackEntry.arguments?.getString("sampleStringDataName")}")
    Log.i("interfacer_han", "${backStackEntry.arguments?.getInt("sampleIntDataName")}")
}

NavBackStackEntry.arguments๋ฅผ ์ฐธ์กฐํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฝ‘์•„๋‚ธ๋‹ค. ๋‚˜๋Š” Log ๋ฉ”์‹œ์ง€๋งŒ ์“ฐ๊ณ  ๋๋ƒˆ์ง€๋งŒ, ์œ„ ์ฝ”๋“œ์— ์žˆ๋Š” SecondScreen๊ณผ ๊ฐ™์€ ์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜์˜ ์ƒ์„ฑ์ž์— ์ „๋‹ฌํ•˜๋Š” ์‹์œผ๋กœ๋„ ์‘์šฉ์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ์ด๋‹ค.
 

#3-6 ์ž‘๋™ ํ™•์ธ (๋กœ๊ทธ ๋ฉ”์‹œ์ง€)

HelloWorld
123

#3-5๊นŒ์ง€์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๋ฉด ๋‚˜์˜ค๋Š” ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋‹ค.
 

#4 ์š”์•ฝ

NavBackStackEntry๋Š” ๋ฐฑ ์Šคํƒ์˜ ํ•œ ์š”์†Œ๋‹ค. NavBackStackEntry๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์˜ ํ˜•์‹์€ ์›น ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ์˜ GET ์š”์ฒญ๊ณผ ๋น„์Šทํ•˜๋‹ค.
 

#5 ์™„์„ฑ๋œ ์•ฑ

android-practice/jetpack-compose/ArgumentsOfNavBackStackEntry at master ยท Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com