๐Ÿ“ฑ๋‚ด๋น„๊ฒŒ์ด์…˜ & ์ œ์Šค์ฒ˜ ์ •๋ฆฌ

HBยท2025๋…„ 5์›” 27์ผ

๐Ÿ’ ๋‚ด๋น„๊ฒŒ์ด์…˜

๋‚ด๋น„๊ฒŒ์ด์…˜์ด๋ž€?

  • ํ™”๋ฉด ๊ฐ„ ์ด๋™์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ
    ex) ๋กœ๊ทธ์ธ ํ™”๋ฉด -> ํ™ˆ ํ™”๋ฉด
  • Jetpack Compose์—์„œ๋Š” ๊ธฐ์กด XML ๋ฐฉ์‹๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜ ์ค‘์‹ฌ์˜ ์„ ์–ธํ˜• ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ œ๊ณต

๋‚ด๋น„๊ฒŒ์ด์…˜ ์ปจํŠธ๋กค๋Ÿฌ์™€ ํ˜ธ์ŠคํŠธ

val navController = rememberNavController()

NavHost(
    navController = navController,
    startDestination = "home"
) {
    composable("home") { HomeScreen(navController) }
    composable("profile") { ProfileScreen() }
}
  • NavController๋ผ๋Š” ๋‚ด๋น„๊ฒŒ์ด์…˜ ์ „์šฉ ์ปจํŠธ๋กค๋Ÿฌ ์„ ์–ธ
    • ํ™”๋ฉด ์ด๋™์„ ์ง€์‹œํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋ฉฐ, NavHost๋ผ๋Š” ๊ตฌ์„ฑ ์š”์†Œ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ

NavController: ํ™”๋ฉด ์ด๋™ ์ œ์–ด
NavHost: ๋ชฉ์ ์ง€๋“ค์„ ์—ฐ๊ฒฐํ•˜๊ณ , ์–ด๋А ํ™”๋ฉด์—์„œ ์‹œ์ž‘ํ• ์ง€๋ฅผ ๊ฒฐ์ •

=> ๋‚ด๋น„๊ฒŒ์ด์…˜ ์‹œ์Šคํ…œ์€ NavController์™€ NavHost๊ฐ€ ์ง์„ ์ด๋ค„ ๋™์ž‘!!

๋ชฉ์ ์ง€ ์ถ”๊ฐ€์™€ ์ธ์ˆ˜ ์ „๋‹ฌ

  • NavHost์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ชฉ์ ์ง€๋ฅผ ๋“ฑ๋ก-> ๊ฐ ํ™”๋ฉด์„ ์ •์˜ ๊ฐ€๋Šฅ
  • ํŠน์ • ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•  ๋•Œ ํ•„์š”ํ•œ ์ •๋ณด ์ „๋‹ฌ ๊ฐ€๋Šฅ
    ex) ์‚ฌ์šฉ์ž ID๋ฅผ ๋„˜๊ฒจ์„œ ํ”„๋กœํ•„ ํŽ˜์ด์ง€์—์„œ ํ•ด๋‹น ์œ ์ € ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ

=> ๋ชฉ์ ์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•จ์œผ๋กœ์จ ํ™”๋ฉด ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๋‚ด๋น„๊ฒŒ์ด์…˜์ด ์™„์„ฑ

ํ•˜๋‹จ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ” ๋‹ค๋ฃจ๊ธฐ

  • ์•ฑ์„ ๋ณด๋‹ค ์ง๊ด€์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ•˜๋‹จ ๋ฐ”๋ฅผ ์‚ฌ์šฉ
  • ํ™ˆ, ์„ค์ •, ๋งˆ์ดํŽ˜์ด์ง€ ๋“ฑ์˜ ํ™”๋ฉด์„ ์•„์ด์ฝ˜์œผ๋กœ ํ‘œ์‹œ
  • Scaffold ์ปดํฌ๋„ŒํŠธ์™€ ํ•จ๊ป˜ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋ฉฐ, ์•„์ดํ…œ์„ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค NavController๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ชฉ์ ์ง€๋กœ ์ด๋™ํ•˜๋„๋ก ์„ค์ •

NavController์™€ BottomNavigationItem์„ ์—ฐ๊ฒฐํ•ด ํ˜„์žฌ ์„ ํƒ๋œ ํ™”๋ฉด์„ ํ‘œ์‹œํ•˜๋Š” ๋กœ์ง์ด ์ค‘์š”!!!



๐Ÿ’  NavigationDemo ํŠœํ† ๋ฆฌ์–ผ

ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ

  • Welcome, Home, Profile ์„ธ ๊ฐœ์˜ ํ™”๋ฉด์„ ๊ตฌํ˜„
  • ๊ฐ ํ™”๋ฉด์€ ํ•˜๋‚˜์˜ ์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค๊ณ , ๋‚ด๋น„๊ฒŒ์ด์…˜์„ ํ†ตํ•ด ํ™”๋ฉด์„ ๋„˜๋‚˜๋“ค ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •

ํ•ต์‹ฌ ๊ฐœ๋…

  • ๊ฒฝ๋กœ๋ฅผ ๋ฌธ์ž์—ด๋กœ ์„ ์–ธ
  • ๊ฐ ๊ฒฝ๋กœ๋งˆ๋‹ค ๋Œ€์‘๋˜๋Š” ํ™”๋ฉด์„ ๋“ฑ๋ก
  • ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด NavController๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ ํ™”๋ฉด์œผ๋กœ ์ด๋™
    ex) ์›ฐ์ปด ํ™”๋ฉด์—์„œ ํ™ˆ ํ™”๋ฉด์œผ๋กœ ์ด๋™ํ•˜๊ณ , ๋‹ค์‹œ ํ™ˆ์—์„œ ํ”„๋กœํ•„ ํ™”๋ฉด์œผ๋กœ ์ „ํ™˜ํ•˜๋Š” ํ๋ฆ„์„ ๋”ฐ๋ผ๊ฐ€๋Š” ๊ตฌ์กฐ



๐Ÿ’  ํ•˜๋‹จ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ” ํŠœํ† ๋ฆฌ์–ผ

BottomBarDemo ํ”„๋กœ์ ํŠธ

@Composable
fun BottomBarDemo() {
    val navController = rememberNavController()
    Scaffold(
        bottomBar = {
            BottomNavigation {
                BottomNavigationItem(
                    icon = { Icon(Icons.Default.Home, contentDescription = null) },
                    label = { Text("Home") },
                    selected = true,
                    onClick = { navController.navigate("home") }
                )
            }
        }
    ) {
        NavHost(navController, startDestination = "home") {
            composable("home") { HomeScreen() }
        }
    }
}
  • ํ•˜๋‹จ ํƒญ UI๋ฅผ ๊ฐ€์ง„ ์•ฑ์„ ๊ตฌ์„ฑ
  • ์‚ฌ์šฉ์ž๋Š” ํ•˜๋‹จ ๋ฐ”์— ์žˆ๋Š” ์—ฌ๋Ÿฌ ์•„์ด์ฝ˜์„ ๋ˆŒ๋Ÿฌ ๊ฐ๊ธฐ ๋‹ค๋ฅธ ํ™”๋ฉด์œผ๋กœ ์ด๋™ ๊ฐ€๋Šฅ
  • Scaffold ๊ตฌ์กฐ ๋‚ด์— bottomBar ์Šฌ๋กฏ ์‚ฌ์šฉ -> ํ™”๋ฉด์˜ ์ƒ๋‹จ, ๋ณธ๋ฌธ, ํ•˜๋‹จ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ๊น”๋”ํ•œ UI ๊ฐ€๋Šฅ

ํ•ต์‹ฌ ๊ฐœ๋…

  • ํ•˜๋‹จ ๋ฐ”์˜ ์•„์ดํ…œ์€ ๋ฆฌ์ŠคํŠธ๋กœ ๋งŒ๋“ค์–ด ๋ฐ˜๋ณต ์ถœ๋ ฅ ๊ฐ€๋Šฅ

  • ํ˜„์žฌ ์„ ํƒ๋œ ํ™”๋ฉด์„ ๊ธฐ์ค€์œผ๋กœ ์•„์ดํ…œ ๊ฐ•์กฐ ํ‘œ์‹œ

  • ์•„์ด์ฝ˜ + ํ…์ŠคํŠธ๋กœ ๊ตฌ์„ฑ๋œ ํƒญ UI


๐Ÿ’  ์ œ์Šค์ฒ˜ ๊ฐ์ง€ํ•˜๊ธฐ

ํด๋ฆญ ์ œ์Šค์ฒ˜

  • ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์ œ์Šค์ฒ˜
  • ํŠน์ • ์˜์—ญ์„ ํ„ฐ์น˜ํ–ˆ์„ ๋•Œ ๋ฐ˜์‘ํ•˜๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ-> Modifier.clickable์„ ์‚ฌ์šฉ
  • ํด๋ฆญ ์‹œ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๋™์ž‘ ์œ ๋„ ๊ฐ€๋Šฅ
    ex) ๋ฒ„ํŠผ์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๋Š” ํ…์ŠคํŠธ

์Šคํฌ๋กค ์ œ์Šค์ฒ˜

  • ํ™”๋ฉด์„ ์œ„์•„๋ž˜๋กœ ์Šคํฌ๋กคํ•˜๋Š” ๋™์ž‘ -> Modifier.scrollable ๋˜๋Š” Modifier.verticalScroll ๊ฐ™์€ ๋ชจ๋””ํŒŒ์ด์–ด๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ ๊ฐ€๋Šฅ
  • ์ƒํƒœ๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ScrollState๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ํ˜„์žฌ ์Šคํฌ๋กค ์œ„์น˜๋„ ์ถ”์  ๊ฐ€๋Šฅ

์ผ๋ฐ˜์ ์ธ UI ์Šคํฌ๋กค์€ verticalScroll, ์‚ฌ์šฉ์ž ์ •์˜ ์Šคํฌ๋กค์€ scrollable์„ ์‚ฌ์šฉ


ํ™•๋Œ€/์ถ•์†Œ, ํšŒ์ „, ์ด๋™ ์ œ์Šค์ฒ˜

  • ์ด๋ฏธ์ง€ ๋ทฐ์–ด๋‚˜ ์ง€๋„๋ฅผ ํ™•๋Œ€/์ถ•์†Œํ•  ๋•Œ ์‚ฌ์šฉ
  • Compose์—์„œ๋Š” pointerInput๊ณผ detectTransformGestures ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

zoom, rotation, pan ๊ฐ’์„ ์ด์šฉํ•ด ์ด๋ฏธ์ง€๋‚˜ ๊ฐ์ฒด์˜ ์†์„ฑ์„ ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝ



๐Ÿ’ ์Šค์™€์ดํ”„ ์ œ์Šค์ฒ˜ ๊ฐ์ง€ํ•˜๊ธฐ

์Šค์™€์ดํ”„๋ž€?

  • ์ขŒ์šฐ ํ˜น์€ ์ƒํ•˜๋กœ ์†๊ฐ€๋ฝ์„ ์›€์ง์—ฌ ํŠน์ • ๋™์ž‘์„ ์œ ๋„ํ•˜๋Š” ์ œ์Šค์ฒ˜
    ex) ๋ฆฌ์ŠคํŠธ์—์„œ ํ•ญ๋ชฉ์„ ์ขŒ์ธก์œผ๋กœ ๋ฐ€๋ฉด ์‚ญ์ œ ๋ฒ„ํŠผ์ด ๋‚˜ํƒ€๋‚˜๋Š” UI ๊ฐ™์€ ๊ฒƒ

์Šค์™€์ดํ”„ ๊ตฌํ˜„ ๊ตฌ์กฐ

  • swipeable ๋ชจ๋””ํŒŒ์ด์–ด๋ฅผ ํ†ตํ•ด ์Šค์™€์ดํ”„ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„
  • ์ƒํƒœ๋ฅผ ์ •์˜ํ•˜๊ณ , ์•ต์ปค๋ผ๋Š” ๊ธฐ์ค€์ ์„ ์„ ์–ธํ•˜์—ฌ ์–ด๋А ์œ„์น˜๊นŒ์ง€ ์Šค์™€์ดํ”„ ๋˜์—ˆ๋Š”์ง€ ํŒ๋‹จ

=> ๊ธฐ์ค€์ ์˜ ์œ„์น˜์™€ ์ƒํƒœ๊ฐ’์„ ์—ฐ๊ฒฐํ•˜๋Š” ์•ต์ปค ๋งต์„ ์ž˜ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”!!

SwipeDemo ํ”„๋กœ์ ํŠธ

val swipeState = rememberSwipeableState(0)
val anchors = mapOf(0f to 0, 300f to 1)

Box(
   modifier = Modifier
       .fillMaxWidth()
       .height(80.dp)
       .swipeable(
           state = swipeState,
           anchors = anchors,
           thresholds = { _, _ -> FractionalThreshold(0.3f) },
           orientation = Orientation.Horizontal
       )
) {
   Text("์Šค์™€์ดํ”„ ํ•ด๋ณด์„ธ์š”", modifier = Modifier.align(Alignment.Center))
}
  • ์Šค์™€์ดํ”„ ๊ฐ€๋Šฅํ•œ ๋ฐ•์Šค ์ƒ์„ฑ
  • swipeable ์ƒํƒœ ์ง€์ •
  • ์ž„๊ณ„์ ์„ ์„ค์ •ํ•ด ์–ด๋А ์ •๋„ ์ด๋™ํ•ด์•ผ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€”์ง€๋ฅผ ์ง€์ •
    => ์‚ฌ์šฉ์ž์˜ ์Šค์™€์ดํ”„ ๋™์ž‘์— ๋”ฐ๋ผ UI๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ฐ˜์‘

0๊ฐœ์˜ ๋Œ“๊ธ€