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

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

interfacer_han 2024. 7. 22. 00:42

#1 Jetpack Compose

#1-1 ๊ฐœ์š”

 

Jetpack Compose UI ์•ฑ ๊ฐœ๋ฐœ ๋„๊ตฌ ํ‚คํŠธ - Android ๊ฐœ๋ฐœ์ž  |  Android Developers

์•ฑ ์ž‘์„ฑ ์†๋„๋ฅผ ๋†’์ด๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” Android์˜ UI ์•ฑ ๊ฐœ๋ฐœ ๋„๊ตฌ ํ‚คํŠธ์™€ ๋ฆฌ์†Œ์Šค์ธ Jetpack Compose๋ฅผ ์‚ดํŽด๋ณด์„ธ์š”.

developer.android.com

์ „ํ†ต์ ์ธ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์กด์˜ ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ UI ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์€ ๋ช…๋ น์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด์—ˆ๋‹ค. XML ๋ ˆ์ด์•„์›ƒ๊ณผ ํ•ด๋‹น ๋ ˆ์ด์•„์›ƒ์„ ์ฐธ์กฐํ•˜๋Š” Java ๋ฐ Kotlin ์ฝ”๋“œ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‹ค. ์ด ๋•Œ, ์ƒˆ๋กญ๊ฒŒ ์ถœ๋ฒ”ํ•œ Jetpack Compose๋Š” ์„ ์–ธ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ, XML ํŒŒ์ผ ์—†์ด ๋ชจ๋“  UI๋ฅผ Kotlin ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•œ๋‹ค (UI์™€ ๋กœ์ง์˜ ํ†ตํ•ฉ).


Jetpack Compose๊ฐ€ ๊ธฐ์กด์˜ ๋ฐฉ์‹๊ณผ ๋น„๊ตํ•˜์—ฌ ์ด์ ์„ ๊ฐ–๋Š” ๋ถ€๋ถ„์€ ๋จผ์ € ์„ ์–ธ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ์˜ ์ „ํ™˜์„ ํ†ตํ•œ ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ(๋†’์€ ๊ฐ€๋…์„ฑ)์™€ ์•”์‹œ์ ์œผ๋กœ ์ง„ํ–‰๋˜๋Š” UI์˜ ์ƒํƒœ(State) ๊ด€๋ฆฌ๋‹ค. ๋˜, XML๊ณผ ๋กœ์ง์„ ํ†ตํ•ฉํ•จ์œผ๋กœ์จ ๋”๋”์šฑ ๋†’์•„์ง„ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด๋‹ค (์ด ๋ถ€๋ถ„์€ ์„ ์–ธ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ์˜ํ•œ ์ด์ ์ด ์•„๋‹ˆ๋ผ ๊ทธ๋ƒฅ Jetpack Compose ์ž์ฒด์˜ ํŠน์„ฑ์ด๋‹ค).

 

#1-2 ์ฝ”๋“œ ์˜ˆ์‹œ

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text("Hello world!")
        }
    }
}

์ด ์ฝ”๋“œ๋Š” ํ™”๋ฉด์— "Hello wolrd!"๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. Text()์ฒ˜๋Ÿผ UI์— ๊ด€์—ฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ Composable์ด๋ผ๊ณ  ํ•˜๋ฉฐ ํ•จ์ˆ˜ ์•ž์— @Composable๋ฅผ ๋ถ™์—ฌ ๊ตฌ๋ถ„ํ•œ๋‹ค.

 

#1-3 Composable์˜ ๊ตฌ์กฐ

@Composable
fun ComposableExample(
    modifier: Modifier,
    ...,
    content: @Composable () -> Unit
): Unit

Composable ํ•จ์ˆ˜์˜ ๊ตฌ์กฐ๋Š” ๋Œ€๋ถ€๋ถ„ ์œ„์™€ ๊ฐ™๋‹ค. Modifier ๋“ฑ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ComposableExample์˜ ์†์„ฑ์„ ์ •์˜ํ•˜๋Š” ๋ฐ์— ์‚ฌ์šฉ๋œ๋‹ค. ๋งจ ๋งˆ์ง€๋ง‰์— ์žˆ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์ธ content๋Š” ComposableExample์˜ ์ž์‹์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. HTML์—์„œ์˜ ์–ด๋–ค ํƒœ๊ทธ์˜ textContent, <h2> ํƒœ๊ทธ๋ฅผ ์˜ˆ๋กœ ๋“ค๋ฉด <h2>Hello!</h2>์˜ Hello!์™€ ๊ฐ™๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋žŒ๋‹ค ํ‘œํ˜„์‹์˜ ๋ฌธ๋ฒ•(์ด ๊ธ€์˜ #2-5 ์ฐธ์กฐ)์— ์˜ํ•ด ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์†Œ๊ด„ํ˜ธ์—์„œ ๋นผ์–ด ์˜ค๋ฅธ์ชฝ์— ๋‘๋ฉด, ComposableExample( ... ) { ... }์™€ ๊ฐ™์€ ๊ตฌ์กฐ๊ฐ€ ๋˜๋Š”๋ฐ, ์ด ๊ตฌ์กฐ๊ฐ€ ์•ž์œผ๋กœ Jetpack Compose์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ๋ณด๊ฒŒ ๋˜๋Š” ํ˜•์‹์ด๋‹ค.

 

#2 Jetpack Compose ์‚ฌ์šฉํ•˜๊ธฐ

#2-1 ๊ฐœ์š”

์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ์‹œ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ณธ Jetpack Compose ํ…œํ”Œ๋ฆฟ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ณ , ํ•ด๋‹น ํ…œํ”Œ๋ฆฟ ์† ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณธ๋‹ค.

 

#2-2 ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ

Empty Activity๋ฅผ ์„ ํƒํ•œ๋‹ค. ํ•ด๋‹น ํ•ญ๋ชฉ์— Jetpack Compose ์•„์ด์ฝ˜๋„ ๋ณด์ธ๋‹ค.

 

#2-3 MainActivity.kt

ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๋ฉด ๋‚ฏ์„  ์ฝ”๋“œ๋“ค์ด ๋“ค์–ด์žˆ๋Š” MainActivity๊ฐ€ ๋ฐ˜๊ฒจ์ค€๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, XML ์ฝ”๋“œ์—์„œ ๋ณด๋˜ [Code / Split / Design] ํƒญ ์ „ํ™˜ ๋ฒ„ํŠผ์ด ๋ณด์ธ๋‹ค. ์ด๋Š” MainActivity๊ฐ€ ๊ทธ ์ž์ฒด๋กœ UI๊นŒ์ง€ ๊ทธ๋ ค๋‚ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

setContent { ... }

Composable UI์˜ ์‹œ์ž‘์ ์œผ๋กœ, ์ด ์˜์—ญ์— @Composable ํ•จ์ˆ˜๋ฅผ ๋„ฃ์œผ๋ฉด UI๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

JetpackComposeBasicsTheme( ... ) { ... }

ui.theme ํŒจํ‚ค์ง€์˜ Theme.kt์—๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์ŠคํŠœ๋””์˜ค๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งŒ๋“ค์–ด๋‘” ์ด ํ”„๋กœ์ ํŠธ์˜ ํ…Œ๋งˆ(Theme)๊ฐ€ ์žˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ "${ํ”„๋กœ์ ํŠธ๋ช…} + Theme"์˜ ์กฐํ•ฉ์œผ๋กœ ์ด๋ฆ„์ด ์ง€์–ด์ง„๋‹ค. ์ด๊ฑธ ์‚ฌ์šฉ์ž ์ž…๋ง›์— ๋งž๊ฒŒ ์ˆ˜์ •ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ผ๋Š” ์˜๋„๋‹ค. ๊ทธ๋ฆฌ๊ณ  MainActivity์— ๊ธฐ๋ณธ์ ์œผ๋กœ built-in๋œ ์ฝ”๋“œ์— ์ด Theme๊ฐ€ ์“ฐ์ด๊ณ  ์žˆ๋‹ค. Theme๋Š” Composable์— ๋Œ€ํ•œ ์ „์—ญ ์„ค์ •์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

 

Surface( ... ) { ... }

Composable๋“ค์˜ Container๋‹ค. Surface( A ) { B }์—์„œ, A๋Š” Surface์— ๋Œ€ํ•œ ์„ค์ •์ด์ง€ B์— ๋Œ€ํ•œ ์„ค์ •์ด ์•„๋‹˜์— ์œ ์˜ํ•œ๋‹ค. B๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” Theme์˜ ์†์„ฑ์„ ์ƒ์†๋ฐ›์•„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ์—๋Š” Greeting()์ด ๋ฐ”๋กœ B์— ํ•ด๋‹นํ•˜๋Š”๋ฐ, Greeting()์— ๋Œ€ํ•œ ์ „์—ญ์  ์„ค์ •์ด ๋ฐ”๋กœ Theme์˜ ๋ชซ์ธ ๊ฒƒ์ด๋‹ค. Surface์˜ ์†์„ฑ์€ ์ด์–ด์ง€๋Š” ๊ฒŒ์‹œ๊ธ€์—์„œ ์ž์„ธํžˆ ๋‹ค๋ฃฌ๋‹ค.

 

Modifier

UI ์†์„ฑ์˜ Setter๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. ์ด์–ด์ง€๋Š” ๊ฒŒ์‹œ๊ธ€์—์„œ ์ž์„ธํžˆ ๋‹ค๋ฃฌ๋‹ค.

 

#2-4 ์ž‘๋™ ํ™•์ธ

์‹คํ–‰์‹œ์ผฐ์„ ๋•Œ์˜ ๋ชจ์Šต์ด๋‹ค.

 

#3 @Preview

#3-1 ๊ฐœ์š”

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    JetpackComposeBasicsTheme {
        Greeting("Android")
    }
}

#2-3์—์„œ ๋งŒ๋“ค์–ด์ง„ ์ฝ”๋“œ ์ค‘์—๋Š” ์œ„์™€ ๊ฐ™์€ @Preview ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํ•จ์ˆ˜๋„ ์žˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ผ์ข…์˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. setContent { ... }์— ๋„ฃ๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜๋ฉฐ, Design ํƒญ์—์„œ Composable์˜ ๋ชจ์Šต์„ ์•ˆ๋“œ๋กœ์ด๋“œ ์—๋ฎฌ๋ ˆ์ดํ„ฐ์˜ ์‹คํ–‰ ์—†์ด ๋ฐ”๋กœ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ์šฉ๋„๋‹ค.

 

#3-2 @Preview์˜ ์ œ์•ฝ

// Default parameter๊ฐ€ ์žˆ๋Š” @Composable ํ•จ์ˆ˜์—๋Š” @Preview๋ฅผ ๋‹ฌ ์ˆ˜ ์žˆ๋‹ค.
@Preview(showBackground = true)
@Composable
fun GreetingPreviewWithDefaultParameter(name: String = "interfacer_han") {
    JetpackComposeBasicsTheme {
        Greeting(name)
    }
}

// ๋งค๊ฐœ๋ณ€์ˆ˜์— @PreviewParameter๊ฐ€ ๋ถ™์€ @Composable ํ•จ์ˆ˜์—๋Š” @Preview๋ฅผ ๋‹ฌ ์ˆ˜ ์žˆ๋‹ค.
@Preview(showBackground = true)
@Composable
fun GreetingPreviewWithProvider(@PreviewParameter(MyParameterProvider::class) name: String) {
    JetpackComposeBasicsTheme {
        Greeting(name)
    }
}

class MyParameterProvider : PreviewParameterProvider<String> {
    override val values = sequenceOf("steve", "kevin")
}

Composable์— @Preview ์–ด๋…ธํ…Œ์ด์…˜์„ ๋‹ค๋Š” ์ˆœ๊ฐ„ Design ํƒญ์—์„œ ๊ทธ ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ œ์•ฝ ์‚ฌํ•ญ์ด ์žˆ๋‹ค. ๋ฐ”๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์—†๊ฑฐ๋‚˜, ์žˆ๋”๋ผ๋„ ๋งค๊ฐœ๋ณ€์ˆ˜์— Default๊ฐ’์ด ์žˆ๊ฑฐ๋‚˜ ํ˜น์€ @PreviewParameter๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์œ„ ์ฝ”๋“œ๋Š” ํ•ด๋‹น ์ œ์•ฝ ์‚ฌํ•ญ์„ ๊ทน๋ณตํ•˜๋Š” ์ฝ”๋“œ๋‹ค.

 

#3-3 Design ํƒญ์—์„œ ํ™•์ธํ•˜๊ธฐ

Design ํƒญ์„ ๋ˆ„๋ฅด๋ฉด Build & Refresh ๋ฒ„ํŠผ์ด ๋‚˜์˜จ๋‹ค. ์ด๋Š” ์ตœ์ดˆ ์ง„์ž… ์‹œ ์“ฐ๋Š” ํ™”๋ฉด์œผ๋กœ ๊ทธ๋ƒฅ Build & Refresh ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ  ๊ธฐ๋‹ค๋ฆฌ๋ฉด ๋œ๋‹ค.

 

์ด๋ ‡๊ฒŒ @Preview ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ Composable๋“ค์˜ ๋ชจ์Šต๋“ค์„ ์•ˆ๋“œ๋กœ์ด๋“œ ์—๋ฎฌ๋ ˆ์ดํ„ฐ ์‹คํ–‰ ์—†์ด ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

#4 ์š”์•ฝ

Jetpack Compose๋Š” UI์™€ ๋กœ์ง์„ ๊ฒฐํ•ฉ์‹œํ‚จ๋‹ค.

 

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

 

android-practice/jetpack-compose/JetpackComposeBasics at master · Kanmanemone/android-practice

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

github.com