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

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

interfacer_han 2024. 7. 22. 18:06

#1 ๊ฐœ์š”

 

์ƒํƒœ ๋ฐ Jetpack Compose  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ƒํƒœ ๋ฐ Jetpack Compose ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ์•ฑ์˜ ์ƒํƒœ๋Š” ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ

developer.android.com

Jetpack Compose์˜ State ๊ฐœ๋…์— ๋Œ€ํ•ด ์‚ดํŽด๋ณธ๋‹ค.
 

#2 ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋…์œผ๋กœ์„œ์˜ State

๋จผ์ €, State๋ผ๋Š” ์šฉ์–ด ์ž์ฒด์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•˜๋‹ค. Jetpack Compose์—์„œ ์“ฐ์ด๋Š” State๊ฐ€ ์•„๋‹ˆ๋ผ ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋…์œผ๋กœ์„œ์˜ State ๋ง์ด๋‹ค. ์ƒํƒœ๋ผ๋Š” ๋ง๋กœ ๋ฒˆ์—ญ๋˜๋Š” State๋Š” ๋ง ๊ทธ๋Œ€๋กœ ์ƒํƒœ, ์ฆ‰ ์‹œ๊ฐ„์ด ํ๋ฆ„์— ๋”ฐ๋ผ ๋ณ€ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๊ฐ’์œผ๋กœ ๋‹ค๋ฆ„์•„๋‹Œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋งํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
 

// ์ผ๋ฐ˜์ ์ธ State๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํด๋ž˜์Šค
class Counter {
    private var count = 0

    // ์ƒํƒœ๋ฅผ ์ฝ๋Š” ํ•จ์ˆ˜
    fun getCount(): Int {
        return count
    }

    // ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ํ•จ์ˆ˜
    fun increment() {
        count++
    }

    fun decrement() {
        count--
    }
}

ํ”„๋กœํผํ‹ฐ count๋ฅผ State๋กœ ๋ณผ ์ˆ˜ ๋„ ์žˆ๊ณ , Counter ํด๋ž˜์Šค๋ฅผ State๋กœ ๋ณผ ์ˆ˜๋„ ์žˆ๋‹ค. State๋ผ๋Š” ์šฉ์–ด ์ž์ฒด๋Š” ๊ต‰์žฅํžˆ ์ถ”์ƒ์ ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
 

#3 Jetpack Compose์—์„œ์˜ State

#3-1 ์กฐ๊ฑด - UI์— ํ‘œ์‹œ๋˜๋Š” ๋ณ€์ˆ˜

๊ทธ๋ ‡๋‹ค๋ฉด ์ด Jetpack Compose์—์„œ ์“ฐ์ด๋Š” State๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ๋”ฑ ๋ด๋„ ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋…์œผ๋กœ์„œ์˜ State๋ณด๋‹ค๋Š” ๋” ๊ตฌ์ฒด์ ์ธ ์˜๋ฏธ๋กœ ์“ฐ์ผ ๊ฒƒ์ด๋‹ค. UI์™€ ๊ด€๋ จ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Jetpack Compose ๋‹ต๊ฒŒ, Jetpack Compose์—์„œ์˜ State๋Š” UI์— ํ‘œ์‹œ๋˜๋Š” ์ˆ˜์น˜, ๋” ์ •ํ™•ํžˆ๋Š” UI์— ํ‘œ์‹œ๋˜๋Š” ๋ณ€์ˆ˜๋‹ค. #2์—์„œ State๋Š” ์‹œ๊ฐ„์ด ํ๋ฆ„์— ๋ณ€ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๊ฐ’์ด๋ผ๊ณ  ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐ’์ด ํ•˜๋“œ ์ฝ”๋”ฉ๋˜์–ด ์žˆ์„ ๋ฆฌ๋Š” ์—†์œผ๋‹ˆ '์ˆ˜์น˜' ๋ณด๋‹ค๋Š” '๋ณ€์ˆ˜'๋ผ๊ณ  ํ•ด์•ผ ๋” ์ •ํ™•ํ•œ ํ‘œํ˜„์ด๋‹ค.
 
ํ•˜์ง€๋งŒ, Jetpack Compose์˜ State๋Š” UI์— ํ‘œ์‹œ๋˜๋Š” ๋ณ€์ˆ˜๋ผ๋Š” ๋ง์€ ๋ถˆ์ถฉ๋ถ„ํ•œ ์„ค๋ช…์ด๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด์ž.
 

var count = 0 // count๋Š” Intํ˜•

@Composable
fun ButtonExample(modifierParam: Modifier = Modifier) {
    Button(
        onClick = {
            Log.i("interfacer_han", "Current count value: ${++count}")
        },
        modifier = modifierParam
    ) {
        Text(
            text = "Count: $count",
            fontSize = 40.sp
        )
    }
}

count๊ฐ€ Text()์—์„œ ์ฐธ์กฐ๋˜๊ณ  ์žˆ๋‹ค. ์ฆ‰, count๋Š” UI์— ํ‘œ์‹œ๋˜๋Š” ๋ณ€์ˆ˜๋‹ค. ์ด ๋•Œ count๋Š” 'State'์ธ๊ฐ€? ์•„๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋…์œผ๋กœ์„œ์˜ State๋Š” ๋งž์ง€๋งŒ, Jetpack Compose์—์„œ ๋งํ•˜๋Š” State๊ฐ€ ๋˜๋ ค๋ฉด ํ•œ ๊ฐ€์ง€ ์กฐ๊ฑด์ด ๋” ํ•„์š”ํ•˜๋‹ค.
 

#3-2 ์กฐ๊ฑด - Observable ํŒจํ„ด ๊ตฌํ˜„

๋ฐ”๋กœ Jetpack Compose์˜ State๋Š” ๋ฐ˜๋“œ์‹œ Observable ํŒจํ„ด์„ ๊ตฌํ˜„ํ•ด์•ผ๋งŒ ํ•œ๋‹ค๋Š” ์กฐ๊ฑด ๋•Œ๋ฌธ์ด๋‹ค. #2์˜ count๋Š” ํ‰๋ฒ”ํ•œ Intํ˜• ๋ณ€์ˆ˜๋กœ Observable ํŒจํ„ด๊ณผ๋Š” ๊ด€๊ณ„๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ State๋ผ๊ณ  ํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด count๋ฅผ Intํ˜•์ด ์•„๋‹ˆ๋ผ Observable ํŒจํ„ด์„ ๊ตฌํ˜„ํ•œ ๋ฐ์ดํ„ฐํ˜•์œผ๋กœ ๋ฐ”๊พธ๋ฉด ๊ทธ๋งŒ์ผ ๊ฒƒ์ด๋‹ค. ๊ทธ ๋ฐ์ดํ„ฐํ˜•์œผ๋กœ๋Š” MutableState, LiveData, Flow ๋˜๋Š” RxJava๊ฐ€ ์žˆ๋‹ค. count๋ฅผ ์ด๋Ÿฌํ•œ ๋ฐ์ดํ„ฐํ˜•์œผ๋กœ ๋ฐ”๊ฟ”๋ณด๋ฉด,
 

val count = mutableStateOf(0) // count๋Š” MutableState<Int>ํ˜•

@Composable
fun ButtonExample(modifierParam: Modifier = Modifier) {
    Button(
        onClick = {
            Log.i("interfacer_han", "Current count value: ${++count.value}")
        },
        modifier = modifierParam
    ) {
        Text(
            text = "Count: ${count.value}",
            fontSize = 40.sp
        )
    }
}

๋น„๋กœ์†Œ count๋Š” (Jetpack Compose์˜) State๊ฐ€ ๋œ๋‹ค.
 

#4 State์˜ ํŠน๊ถŒ

#4-1 ํž˜๋“ค๊ฒŒ(?) State๋ฅผ ๋งŒ๋“ค์–ด ๋‘๋Š” ์ด์œ 

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

 

State์˜ ํŠน๊ถŒ์„ ์ฒด๊ฐํ•˜๊ธฐ ์œ„ํ•ด์„œ, ์‹ค์ œ ์•ฑ์„ ์‹คํ–‰์‹œ์ผœ ๊ทธ ๋™์ž‘์„ ์‚ดํŽด๋ณด๊ฒ ๋‹ค. ์œ„์—์„œ ๋‹ค๋ค˜๋˜ count ๋ณ€์ˆ˜๊ฐ€, State์ธ ๊ฒฝ์šฐ์™€ State๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ ๊ฐ๊ฐ์„ ์•ˆ๋“œ๋กœ์ด๋“œ ์—๋ฎฌ๋ ˆ์ดํ„ฐ๋กœ ์‹คํ–‰์‹œํ‚จ๋‹ค.

#4-2 ๋ณ€์ˆ˜ count๊ฐ€ State๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ์˜ ๋™์ž‘

count๊ฐ€ State๊ฐ€ ์•„๋‹ˆ์—ˆ๋˜ #3-1๋ฅผ ์‹ค์ œ๋กœ ์‹คํ–‰์‹œํ‚จ ๋‹ค์Œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์ผ์–ด๋‚˜๋Š” ์ผ์ด๋‹ค. Log ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋ฉด count๋Š” ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ๋ถ„๋ช…ํžˆ ์ฆ๊ฐ€ํ•˜๊ณ  ์žˆ์ง€๋งŒ, UI์—์„œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ด๋ค„์ง€์ง€ ์•Š์•„ count๊ฐ€ ๊ณ„์† 0์ธ ๊ฑธ๋กœ ๋ณด์ธ๋‹ค. ๋ฌผ๋ก  ์• ์ดˆ์— Button์˜ ํด๋ฆญ๋ฆฌ์Šค๋„ˆ์—์„œ Text ์—…๋ฐ์ดํŠธ์— ๊ด€ํ•œ ์ฝ”๋“œ๋ฅผ ์จ ๋„ฃ์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด์ง€๋งŒ ๋ง์ด๋‹ค (์—ฌ๋‹ด์ด์ง€๋งŒ ์‚ฌ์‹ค, Jetpack Compose๋Š” ์„ ์–ธ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋ผ์„œ Button() ํด๋ฆญ ์‹œ ๊ทธ ๋‚ด๋ถ€์˜ Text()๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์งœ๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ์„ค๊ณ„์ƒ ๋ถˆ๊ฐ€๋Šฅ์— ๊ฐ€๊น๋‹ค ๋”ฐ๋ผ์„œ, ์–ด์ฐŒ๋ณด๋ฉด Jetpack Compose์—์„œ์˜ State ์‚ฌ์šฉ์€ ํŠน๊ถŒ์ด๋ผ๊ธฐ๋ณด๋‹จ ์˜๋ฌด๋ผ๊ณ  ๋ด์•„ํ•  ์ง€๋„ ๋ชจ๋ฅธ๋‹ค).
 

#4-3 ๋ณ€์ˆ˜ count๊ฐ€ State์ธ ๊ฒฝ์šฐ์˜ ๋™์ž‘

๋ฐ˜๋ฉด, count๊ฐ€ State์˜€๋˜ #3-2์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ฉด UI ์—…๋ฐ์ดํŠธ๊ฐ€ ์ด๋ค„์ง„๋‹ค. count๊ฐ€ State์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. Compose ๋Ÿฐํƒ€์ž„์€ ์ƒˆ๋กœ์šด State์˜ ๋ฐ์ดํ„ฐ๋กœ Composable์„ ๋‹ค์‹œ ์‹คํ–‰ํ•ด ์—…๋ฐ์ดํŠธ๋œ UI๋ฅผ ํ™”๋ฉด์— ์ถœ๋ ฅํ•œ๋‹ค. ์ด๋ฅผ ์žฌ๊ตฌ์„ฑ(Recomposition)์ด๋ผ๊ณ  ํ•œ๋‹ค.
 

#5 LiveData์™€์˜ ๋น„๊ต ์šฐ์œ„

#5-1 ๊ฐ„๊ฒฐํ•จ (์„ ์–ธ์ )

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

 

#5-2 Jetpack Compose์™€์˜ ํ†ตํ•ฉ์„ฑ

State๋Š” Jetpack Compose์™€์˜ ํ†ตํ•ฉ์„ ๊ณ ๋ คํ•˜์—ฌ ์„ค๊ณ„๋˜์—ˆ๋‹ค. Compose์™€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๋™๋˜์–ด ์ถ”๊ฐ€์ ์ธ Observer ์„ค์ •์ด๋‚˜ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ ์—†์ด State.value์˜ ๋ณ€ํ™”์— ๋”ฐ๋ผ UI๊ฐ€ ์ž๋™์œผ๋กœ ์žฌ๊ตฌ์„ฑ(Recomposition)๋œ๋‹ค.

 

#6 ์š”์•ฝ

UI์— ํ‘œ์‹œ๋˜๋Š” ๋ณ€์ˆ˜๋ฉด์„œ, ๋™์‹œ์— Observable ํŒจํ„ด๊นŒ์ง€ ์ถฉ์กฑํ•ด์•ผ๋งŒ ๋น„๋กœ์†Œ Jetpack Compose์˜ State๋‹ค. State.value๊ฐ€ ๋ณ€ํ•˜๋ฉด Recomposition์ด ์•”์‹œ์ ์œผ๋กœ ์ˆ˜ํ–‰๋œ๋‹ค.
 

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

 

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

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

github.com

#3-1 ~ #3-2์˜ ์ฝ”๋“œ๊ฐ€ ๋‹ด๊ธด ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ์ ํŠธ๋‹ค.