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

[Android] Jetpack Compose - State Hoisting

interfacer_han 2024. 7. 23. 01:55

#1 ๊ฐœ์š”

 

์ƒํƒœ๋ฅผ ํ˜ธ์ด์ŠคํŒ…ํ•  ๋Œ€์ƒ ์œ„์น˜  |  Jetpack Compose  |  Android Developers

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

developer.android.com

State Hoisting(์ƒํƒœ ํ˜ธ์ด์ŠคํŒ…) ํŒจํ„ด์„ ์ ์šฉํ•ด ์ฝ”๋“œ์˜ ์ž ์žฌ์  ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’ํ˜€๋ณธ๋‹ค.
 

#2 ์ฝ”๋“œ

#2-1 State Hoisting ํŒจํ„ด์ด ์ ์šฉ๋˜์ง€ ์•Š์€ ์ฝ”๋“œ

...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Box(
                modifier = Modifier.fillMaxSize()
            ) {
                ButtonExample(Modifier.align(Alignment.Center))
            }
        }
    }
}

// โ†“ โ†“ โ†“ Stateful Composable
@Composable
fun ButtonExample(modifierParam: Modifier = Modifier) {
    val count = remember {
        mutableStateOf(0)
    }

    Button(
        onClick = { count.value++ },
        modifier = modifierParam
    ) {
        Text(
            text = "Count: ${count.value}",
            fontSize = 40.sp
        )
    }
}

์œ„ ์ฝ”๋“œ์˜ ButtonExample()์ฒ˜๋Ÿผ State๋ฅผ ๋ณด์œ ํ•œ Composable์„ Stateful Composable์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. Stateful Composable์€ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋–จ์–ด์ง€๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ๋„ ์–ด๋ ต๋‹ค๋Š” ๋ฌธ์ œ๋ฅผ ๊ฐ€์ง„๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ์˜์กด์„ฑ ์ฃผ์ž…์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด State๋ฅผ ์™ธ๋ถ€ ์ƒ์„ฑ์ž๋กœ๋ถ€ํ„ฐ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ์‹์œผ๋กœ, ์ฆ‰ Composable์ด ์ƒ์„ฑ์ž ์ฃผ์ž…(Constructor Injection)์„ ๊ตฌํ˜„ํ•˜๊ฒŒ๋” ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค.
 

#2-2 State Hoisting ํŒจํ„ด์ด ์ ์šฉ๋œ ์ฝ”๋“œ

...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Box(
                modifier = Modifier.fillMaxSize(),
            ) {
                val count = remember {
                    mutableStateOf(0)
                }

                ButtonExample(
                    count.value,
                    Modifier.align(Alignment.Center)
                ) { newValue ->
                    count.value = newValue + 1
                }
            }
        }
    }
}

// โ†“ โ†“ โ†“ Stateless Composable
@Composable
fun ButtonExample(
    currentCount: Int,
    modifierParam: Modifier = Modifier,
    updateCount: (Int) -> Unit,
) {
    Button(
        onClick = { updateCount(currentCount) }, // updateCount()๋Š” ButtonExample()์ด ์•„๋‹Œ, ButtonExample()๋ฅผ ํ˜ธ์ถœํ•œ ์ƒ์œ„ Composable์—์„œ ์ฒ˜๋ฆฌ๋œ๋‹ค.
        modifier = modifierParam

    ) {
        Text(
            text = "Count: $currentCount",
            fontSize = 40.sp
        )
    }
}

State๊ฐ€ ์žˆ๋Š” Composable์ด Stateful Composable์ด์—ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ, State๊ฐ€ ์—†๋Š” Composable์€ Stateless Composable๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. State๋ฅผ ์ƒ์„ฑ์ž ์ฃผ์ž…๋ฐ›๋Š” ๊ฒฝ์šฐ๋„ Stateless Composable์ด๋‹ค. ์šฐ์„  State๋ฅผ setContent { ... }๋กœ ์ด๋™์‹œํ‚ค๊ณ  ButtonExample์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— State์˜ ์ œ๋„ค๋ฆญ ํƒ€์ž…(์—ฌ๊ธฐ์„œ๋Š” Int)์ธ currentCount๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ฒ„ํŠผ์˜ ํด๋ฆญ ๋ฆฌ์Šค๋„ˆ์— ๋„ฃ์„ ๋žŒ๋‹ค ํ•จ์ˆ˜๋„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค. ์ด ๋žŒ๋‹ค ํ•จ์ˆ˜๋Š” setContent { ... }์— ์žˆ๋Š” State์˜ ๊ฐ’์„ ์กฐ์ž‘ํ•œ๋‹ค.
 
์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„ ์œ„ ์ฝ”๋“œ์˜ ๊ตฌ์กฐ๋ฅผ State Hoisting ํŒจํ„ด์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. hoist์˜ ์‚ฌ์ „์  ์˜๋ฏธ๋Š” (ํ”ํžˆ ๋ฐง์ค„์ด๋‚˜ ์žฅ๋น„๋ฅผ ์ด์šฉํ•˜์—ฌ) ๋“ค์–ด[๋Œ์–ด]์˜ฌ๋ฆฌ๋‹ค์ธ๋ฐ, State๋ฅผ ํ•˜์œ„ Composable (๋ณธ ๊ฒŒ์‹œ๊ธ€์—์„œ๋Š” ButtonExample())๋กœ๋ถ€ํ„ฐ ์ƒ์œ„ Composable (๋ณธ ๊ฒŒ์‹œ๊ธ€์—์„œ๋Š” setContent)๊นŒ์ง€ ๋Œ์–ด ๋‹น๊ธด๋‹ค๋Š” ๋Šฌ์•™์Šค๋กœ ์“ฐ์ธ ๊ฒƒ์ด๋‹ค.
 

#3 State Hoisting ํŒจํ„ด์˜ ์˜์˜

State Hoisting์€ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„(Unidirectional Data Flow)์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ์ฃผ์š”ํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด์ด๋‹ค. ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด๋ž€, ์ƒํƒœ(State)๋Š” ์œ„(setContent)์—์„œ ์•„๋ž˜(ButtonExample())๋กœ ๋ณด๋‚ด๊ณ , ์ด๋ฒคํŠธ(ํด๋ฆญ ์ด๋ฒคํŠธ ๋“ฑ)์€ ์•„๋ž˜(ButtomExample())์—์„œ ์œ„(setContent)๋กœ ๋ณด๋‚ด๋Š” ํ๋ฆ„์„ ์˜๋ฏธํ•œ๋‹ค. ๋”ฐ๋ผ์„œ State Hoisting ํŒจํ„ด์„ ์ ์šฉํ•  Composable์—๊ฒŒ๋Š” 2๊ฐ€์ง€๊ฐ€ ์š”๊ตฌ๋œ๋‹ค. Composable์ด ํ˜„์žฌ ํ‘œ์‹œํ•  ๊ฐ’๊ณผ ๊ทธ ๊ฐ’์„ ๋ณ€๊ฒฝ ์š”์ฒญํ•˜๋Š” ์ด๋ฒคํŠธ๋‹ค. ์ „์ž๋Š” #2-2์˜ currentCount์— ํ•ด๋‹นํ•˜๊ณ  ํ›„์ž๋Š” #2-2์˜ updateCount์— ํ•ด๋‹นํ•œ๋‹ค.
 
๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด ๊ตฌ์ถ•๋˜๋ฉด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ State๋ฅผ ๋ฐ›์•„์™€ Staless Composable์— ์ฃผ์ž…์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ ์บก์Šํ™” ๋ฐ ๋ชจ๋“ˆํ™”๊ฐ€ ๋‹ฌ์„ฑ๋œ๋‹ค. ๋˜ State๊ฐ€ ์ƒ์œ„ Composable์— ์œ„์น˜ํ•˜๊ธฐ์— ๋” ์ค‘์•™์ง‘์ค‘์ ์ธ ์œ„์น˜์—์„œ State๋ฅผ ์ผ๊ด„์ ์œผ๋กœ, ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.
 

#4 ์š”์•ฝ

State Hoisting์€ State๋ฅผ ์ƒ์œ„ Composable๋กœ ์˜ฎ๊ธฐ๋Š” ์ฝ”๋“œ ํŒจํ„ด์ด๋‹ค.

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

 

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

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

github.com

 

#6 ์ด์–ด์ง€๋Š” ๊ธ€

 

[Android] Jetpack Compose - ViewModel์—์„œ State ์‚ฌ์šฉํ•˜๊ธฐ

#1 ๊ฐœ์š”Jetpack Compose์—์„œ ViewModel์„ ์‚ฌ์šฉํ•ด๋ณธ๋‹ค. Jetpack Compose๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ „ํ†ต์ ์ธ ๋ฐฉ์‹์—์„œ์˜ ViewModel๊ณผ ํฌ๊ฒŒ ๋‹ค๋ฅผ ๊ฒŒ ์—†๋‹ค. Jetpack Compose์— ViewModel์„ ๊ตฌํ˜„ํ•จ์œผ๋กœ์จ State Hoisting ํŒจํ„ด์„ ๊ทน๋Œ€ํ™”์‹œ

kenel.tistory.com

ViewModel์„ ์ด์šฉํ•จ์œผ๋กœ์จ, State Hoisting ํŒจํ„ด์„ ์ฆ๋Œ€์‹œํ‚จ๋‹ค.