๐Ÿ“ฑAndroid ๊ฐœ๋ฐœ์„ ์œ„ํ•œ Kotlin & Jetpack Compose

HBยท2025๋…„ 4์›” 9์ผ

๐Ÿ’ Kotlin์ด๋ž€ ?

  • ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์„ ๋งŒ๋“ค ๋•Œ ๊ฑฐ์˜ ํ•„์ˆ˜์ฒ˜๋Ÿผ ์“ฐ์ด๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด, 2017๋…„๋ถ€ํ„ฐ Google์ด ๊ณต์‹ Android ๊ฐœ๋ฐœ ์–ธ์–ด๋กœ ์ฑ„ํƒํ–ˆ์„ ๋งŒํผ ์ค‘์š”๋„๊ฐ€ ๋†’์Œ

  • ํŠน์ง•

    • ๊ฐ„๊ฒฐํ•จ: ์ฝ”๋“œ๋ฅผ ์งง๊ณ  ๊น”๋”ํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ์Œ

    • ์•ˆ์ •์„ฑ: ๋„(null) ๊ด€๋ จ ์—๋Ÿฌ ๊ฐ์†Œ

    • ํ˜„๋Œ€์ ์ธ ๋ฌธ๋ฒ•: ๋žŒ๋‹ค, ํ™•์žฅ ํ•จ์ˆ˜, ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค ๋“ฑ ์ตœ์‹  ๋ฌธ๋ฒ• ์ง€์›

    • Java์™€ 100% ํ˜ธํ™˜ ๊ฐ€๋Šฅ


๋ณ€์ˆ˜ ์„ ์–ธ & ๋ฐ์ดํ„ฐ ํƒ€์ž…

  • Kotlin์—์„œ๋Š” ๋ณ€์ˆ˜ ์„ ์–ธ ์‹œ val ๋˜๋Š” var๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
val name: String = "ํ™๊ธธ๋™"  // ์ฝ๊ธฐ ์ „์šฉ (๋ถˆ๋ณ€)
var age: Int = 24         // ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ

val : ํ•œ ๋ฒˆ ์ •ํ•˜๋ฉด ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋Š” ๊ฐ’
var : ๋‚˜์ค‘์— ๊ฐ’์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋Š” ๋ณ€์ˆ˜

๋ณดํ†ต ๋ณ€๊ฒฝํ•  ํ•„์š” ์—†๋Š” ๊ฐ’์€ val๋กœ, ๊ผญ ๋ฐ”๋€Œ์–ด์•ผ ํ•˜๋Š” ๊ฐ’๋งŒ var๋กœ!


  • Kotlin์€ ํƒ€์ž…์„ ๋ช…์‹œํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ ์ถ”๋ก ํ•ด์คŒ!
val message = "์•ˆ๋…•"  // String์œผ๋กœ ์ž๋™ ์ถ”๋ก 
val count = 5         // Int๋กœ ์ถ”๋ก 

๋„(null) ์•ˆ์ •์„ฑ

Kotlin์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ํŠน์ง• ์ค‘ ํ•˜๋‚˜๋Š” ๋„ ์•ˆ์ •์„ฑ์ด๋‹ค.

  • ๋„์ผ ์ˆ˜๋„ ์žˆ๋Š” ๊ฐ’์— ์ ‘๊ทผํ•  ๋• ?. ์ด๋‚˜ ?:๋ฅผ ํ™œ์šฉ
var nickname: String? = null  // ?๋ฅผ ๋ถ™์ด๋ฉด null ํ—ˆ์šฉ

  • ์•„์˜ˆ null์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์„ค๊ณ„๊ฐ€๋Šฅ
println(nickname?.length ?: 0)  // null์ด๋ฉด 0 ์ถœ๋ ฅ



๐Ÿ’ ์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜

Jetpack Compose๋Š” ์„ ์–ธํ˜• UI ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋Š” Android UI ํˆดํ‚ท์œผ๋กœ, ์ด์ „์— ๋งํ–ˆ๋˜ ๊ฒƒ๊ณผ ๊ฐ™์ด XML ์—†์ด๋„ ์ฝ”๋“œ๋กœ UI๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜๋ž€?

@Composable ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํ•จ์ˆ˜๋Š” UI๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ธ”๋ก์œผ๋กœ,
์‰ฝ๊ฒŒ ๋งํ•ด์„œ XML๋กœ UI๋ฅผ ์งœ๋˜ ๊ฑธ ํ•จ์ˆ˜๋กœ ์ง์ ‘ ๊ทธ๋ฆฐ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

@Composable
fun HelloText() {
    Text(text = "์•ˆ๋…•ํ•˜์„ธ์š”!")
}
  • ์ด ํ•จ์ˆ˜๋Š” ํ™”๋ฉด์— ํ…์ŠคํŠธ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• 

  • Text, Button, Column ๋“ฑ Compose ์ „์šฉ UI ์š”์†Œ๋“ค์€ Composable ํ•จ์ˆ˜ ์•ˆ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ!


์ƒํƒœ ์ปดํฌ์ €๋ธ” vs ๋น„์ƒํƒœ ์ปดํฌ์ €๋ธ”

์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜๋Š” ์ƒํƒœ(State)๋ฅผ ๊ฐ€์ง€๋Š”์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“Œ ๋น„์ƒํƒœ ์ปดํฌ์ €๋ธ”

  • UI๋งŒ ์กด์žฌ , ๋‚ด๋ถ€ ์ƒํƒœ๋Š” ์—†์Œ
@Composable
fun Title() {
    Text("Compose")
}

๐Ÿ“Œ ์ƒํƒœ ์ปดํฌ์ €๋ธ”

  • UI + ์ƒํƒœ ๋ณด์œ , ๊ฐ’ ๋ณ€ํ™”์— ๋”ฐ๋ผ ์ž๋™ ์žฌ๊ตฌ์„ฑ๋จ

  • remember์™€ mutableStateOf๋ฅผ ์ด์šฉํ•ด ์ƒํƒœ ์ €์žฅ

  • ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ์ž๋™์œผ๋กœ ํ•ด๋‹น UI๋งŒ ๋‹ค์‹œ ๊ทธ๋ฆผ

@Composable
fun ClickCounter() {
    var count by remember { mutableStateOf(0) }

    Button(onClick = { count++ }) {
        Text("Clicked $count times")
    }
}



์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜ ๊ตฌ๋ฌธ ์ •๋ฆฌ

  • ๋งค๊ฐœ๋ณ€์ˆ˜ ์ „๋‹ฌ ๊ฐ€๋Šฅ
@Composable
fun Greeting(name: String) {
    Text("Hello, $name!")
}
  • ์ค‘์ฒฉ ํ˜ธ์ถœ ๊ฐ€๋Šฅ
@Composable
fun Screen() {
    Greeting("๊ธธ๋™")
    Greeting("ํ™๊ธธ๋™")
}
  • UI ์ปดํฌ๋„ŒํŠธ ์กฐํ•ฉ ๊ฐ€๋Šฅ
@Composable
fun MyScreen() {
    Column {
        Text("์ œ๋ชฉ")
        Button(onClick = { /* ์ด๋ฒคํŠธ */ }) {
            Text("ํด๋ฆญ!")
        }
    }
}

ํŒŒ์šด๋ฐ์ด์…˜(Foundation) ์ปดํฌ์ €๋ธ”

ํŒŒ์šด๋ฐ์ด์…˜ ์ปดํฌ๋„ŒํŠธ๋Š” Compose์˜ ๊ธฐ๋ณธ์ ์ธ UI ๊ตฌ์„ฑ์š”์†Œ
์ฃผ๋กœ ๋ฐฐ์น˜, ํฌ๊ธฐ, ์ •๋ ฌ ๋“ฑ ๋ ˆ์ด์•„์›ƒ์˜ ๋ผˆ๋Œ€๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

  • Text(): ํ…์ŠคํŠธ ์ถœ๋ ฅ
  • Row {} : ๊ฐ€๋กœ๋กœ ๋‚˜์—ด
  • Column {} : ์„ธ๋กœ๋กœ ๋‚˜์—ด
  • Box {} : ๊ฒน์น˜๊ธฐ, ์ž์œ  ๋ฐฐ์น˜
  • Spacer() : ๋นˆ ๊ณต๊ฐ„ ์‚ฝ์ž…
  • Image() : ์ด๋ฏธ์ง€ ์ถœ๋ ฅ
@Composable
fun SimpleLayout() {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("์•ˆ๋…•ํ•˜์„ธ์š”")
        Spacer(modifier = Modifier.height(10.dp))
        Text("๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค")
    }
}

๋จธํ‹ฐ๋ฆฌ์–ผ(Material) ์ปดํฌ์ €๋ธ”

Material ์ปดํฌ์ €๋ธ”์€ ๊ตฌ๊ธ€์˜ ๋จธํ‹ฐ๋ฆฌ์–ผ ๋””์ž์ธ ์ฒ ํ•™์„ ์ ์šฉํ•œ ์ปดํฌ๋„ŒํŠธ
๋ฒ„ํŠผ, ์นด๋“œ, ์Šค๋‚ต๋ฐ” ๋“ฑ ์ต์ˆ™ํ•œ UI๋“ค์ด ์—ฌ๊ธฐ์— ํฌํ•จ๋œ๋‹ค.

  • Button() : ๋ฒ„ํŠผ
  • Card() : ๊ทธ๋ฆผ์ž/๋‘ฅ๊ทผ UI ์นด๋“œ
  • Scaffold() : ์ „์ฒด ๋ ˆ์ด์•„์›ƒ ํ‹€
  • TopAppBar() : ์ƒ๋‹จ ์•ฑ ๋ฐ”
  • FloatingActionButton() : FAB ๋ฒ„ํŠผ
  • Snackbar() : ํ•˜๋‹จ ๋ฉ”์‹œ์ง€
@Composable
fun MaterialSample() {
    Scaffold(
        topBar = {
            TopAppBar(title = { Text("My App") })
        },
        floatingActionButton = {
            FloatingActionButton(onClick = {}) {
                Icon(Icons.Default.Add, contentDescription = "์ถ”๊ฐ€")
            }
        }
    ) {
        Text("๋ฉ”์ธ ํ™”๋ฉด")
    }
}

Scaffold๋Š” ๊ธฐ๋ณธ ๋ ˆ์ด์•„์›ƒ์„ ์ •๋ฆฌํ•  ๋•Œ ํ•„์ˆ˜ ์š”์†Œ!
์•ฑ์˜ ์ƒ๋‹จ ๋ฐ”, ํ•˜๋‹จ ๋ฐ”, ํ”Œ๋กœํŒ… ๋ฒ„ํŠผ ๋“ฑ์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.

  • @Composable ํ•จ์ˆ˜๋Š” Compose์˜ UI ๋‹จ์œ„!
  • ์ƒํƒœ ์œ ๋ฌด์— ๋”ฐ๋ผ ๋™์ž‘์ด ๋‹ฌ๋ผ์ง
  • ํŒŒ์šด๋ฐ์ด์…˜์€ ๋ ˆ์ด์•„์›ƒ, ๋จธํ‹ฐ๋ฆฌ์–ผ์€ UI ์ปดํฌ๋„ŒํŠธ์— ์ดˆ์ 

์ฝ”ํ‹€๋ฆฐ์˜ ๊ธฐ์ดˆ ๋ฌธ๋ฒ•๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜์™€ ์ƒํƒœ ๊ด€๋ฆฌ, ๋จธํ‹ฐ๋ฆฌ์–ผ ์ปดํฌ๋„ŒํŠธ๊นŒ์ง€ ์ „๋ฐ˜์ ์ธ ๊ฐœ์š”๋ฅผ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค. ์•ž์œผ๋กœ ๋” ์‹ฌํ™”๋œ ๊ฐœ๋…๋“ค๊ณผ ์˜ˆ์ œ๋“ค์„ ๊ณต๋ถ€ํ•˜๋ฉฐ ์‹ค์ œ ์•ฑ ๊ฐœ๋ฐœ๊นŒ์ง€ ๊ฐ€๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค!

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