๐ŸŒ€ derivedStateOf ์˜ Trade-Off

Ryanยท2026๋…„ 5์›” 7์ผ

Android

๋ชฉ๋ก ๋ณด๊ธฐ
7/7
post-thumbnail

1๏ธโƒฃ derivedStateOf๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

ํ•œ ์ค„ ์ •์˜

derivedStateOf๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ Compose State๋กœ๋ถ€ํ„ฐ "ํŒŒ์ƒ๋œ ์ƒํƒœ"๋ฅผ ๋งŒ๋“ค๊ณ ,
๊ฒฐ๊ณผ ๊ฐ’์ด ์‹ค์ œ๋กœ ๋ฐ”๋€Œ์—ˆ์„ ๋•Œ๋งŒ ๋ฆฌ์ปดํฌ์ง€์…˜์„ ์œ ๋ฐœํ•˜๋Š” Effect API

var text by remember { mutableStateOf("") }
val filteredText by remember {
    derivedStateOf {
        text.filter { !it.isDigit() }
    }
}

ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด์„œ text ์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋ ๋•Œ ๋‚ด๋ถ€ ํ•จ์ˆ˜๋กœ ๋“ฑ๋ก๋œ filter ๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

text ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค derivedStateOf ์•ˆ์˜ ์‹์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๊ณ , ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์กŒ์„ ๋•Œ๋งŒ UI๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.

2๏ธโƒฃ ์™œ derivedStateOf๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

Compose์˜ ๊ธฐ๋ณธ ๋™์ž‘

State๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ํ•ด๋‹น State๋ฅผ ์ฝ์€ Composable์€ย ๋ฌด์กฐ๊ฑด ๋ฆฌ์ปดํฌ์ง€์…˜๋ฉ๋‹ˆ๋‹ค.

val text by remember { mutableStateOf("") }
val filteredTextby = remember(text) { 
    text.filter { !it.isDigit() }
}

text๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹คย filteredText๋Š” ์žฌ๊ณ„์‚ฐ๋˜๊ณ  ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” UI๋Š” ๋‹ค์‹œ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š”ย text๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ํ•„ํ„ฐ๋ง ๊ฒฐ๊ณผ๊ฐ€ ๋™์ผํ•œ ๊ฒฝ์šฐ์—๋„ ๋ฆฌ์ปดํฌ์ง€์…˜์ด ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์œ„ ์ฝ”๋“œ๋ฅผ ์˜ˆ์‹œ๋กœ ํ…์ŠคํŠธ์—์„œ ์ˆซ์ž๋งŒ ํ•„ํ„ฐํ• ๋•Œ, ย "abc"ย โ†’ย "abcd"ย โ†’ย "abc"ย ์ฒ˜๋Ÿผ ์ˆซ์ž๊ฐ€ ์—†๋Š” ์ƒํƒœ๊ฐ€ ๊ณ„์† ์œ ์ง€๋˜์–ด๋„, text๊ฐ€ ๋ฐ”๋€Œ๋Š” ๋งค ์ˆœ๊ฐ„ UI๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์œผ๋ฉด ์‹ค์ œ UI ๋ณ€ํ™”๊ฐ€ ์—†๋Š”๋ฐ๋„ ๋‹ค์‹œ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

3๏ธโƒฃ derivedStateOf์˜ ํ•ต์‹ฌ ๋™์ž‘ ๊ตฌ์กฐ

๋‚ด๋ถ€์ ์œผ๋กœ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๋Š”๊ฐ€?

@StateFactoryMarker
public fun <T> derivedStateOf(calculation: () -> T): State<T> =
    DerivedSnapshotState(calculation, null)

derivedStateOf๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ DerivedSnapshotState๋ผ๋Š” ํŠน์ˆ˜ํ•œ State ๊ตฌํ˜„์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

DerivedSnapshotState๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฑ…์ž„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

  • ๊ณ„์‚ฐ ๋žŒ๋‹ค(calculation) ๋ณด๊ด€
  • ๊ณ„์‚ฐ ๊ฒฐ๊ณผ ์บ์‹ฑ
  • ๊ณ„์‚ฐ ์ค‘ ์ฝํžŒ State ์ž๋™ ์ถ”์ 
  • Snapshot ๋ณ€๊ฒฝ ์‹œ ์บ์‹œ ์œ ํšจ์„ฑ ํŒ๋‹จ
  • ํ•„์š”ํ•  ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐ ์ˆ˜ํ–‰

์ด ๋ชจ๋“  ๋กœ์ง์€ ๋‚ด๋ถ€์˜ ResultRecord๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

class ResultRecord<T>(snapshotId: SnapshotId) : StateRecord(snapshotId), DerivedState.Record<T> {
     override var dependencies: ObjectIntMap<StateObject> = emptyObjectIntMap()
     var result: Any? = Unset
     var resultHash: Int = 0
     ...
}

result: ๋งˆ์ง€๋ง‰ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ

dependencies: ๊ณ„์‚ฐ ์ค‘ ์ฝํžŒ State ๋ชฉ๋ก

resultHash: dependencies๋“ค์ด ํ˜„์žฌ snapshot ์—์„œ๋„ ๊ฐ™์€ ์ƒํƒœ์ธ์ง€ ํŒ๋‹จํ•˜๊ธฐ ์œ„ํ•œ ํ•ด์‹œ

DerivedSnapshotState ๊ตฌ์กฐ

fun isValid(derivedState: DerivedState<*>, snapshot: Snapshot): Boolean {
    val snapshotChanged = sync {
        validSnapshotId != snapshot.snapshotId ||
            validSnapshotWriteCount != snapshot.writeCount
    }
    val isValid =
        result !== Unset &&
            (!snapshotChanged || resultHash == readableHash(derivedState, snapshot))

    if (isValid && snapshotChanged) {
        sync {
            validSnapshotId = snapshot.snapshotId
            validSnapshotWriteCount = snapshot.writeCount
        }
    }

    return isValid
}

ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ํ˜„์žฌ ResultRecord ๊ฐ€ ์ฃผ์–ด์ง„ snapshot ์‹œ์ ์—์„œ ์ตœ์‹  ์ƒํƒœ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๋‹น ๋ถ€๋ถ„์— ์ตœ์‹  ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด Snapshot.observe() ๋ฅผ ํ†ตํ•ด์„œ calculation()์„ ํ˜ธ์ถœํ•ด ์ƒˆ๋กœ์šด dependencies ์™€ result ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

derivedStateOf๋Š” state ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋Œ€์‹  โ€œ๊ณ„์‚ฐ + ๋น„๊ตโ€ ๋น„์šฉ์„ ํ•ญ์ƒ ๊ฐ์ˆ˜ํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

โš ๏ธ ์ค‘์š”ํ•œ ์„ฑ๋Šฅ ๊ด€์ 

์›ํ™œํ•œ ์„ฑ๋Šฅ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด

derivedStateOf ์•ˆ์— ์•„์ฃผ ๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ์‹์„ ๋„ฃ๋Š” ๊ฒƒ์€ ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—ฐ์‚ฐ์ด ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก derivedStateOf์˜ โ€œ๊ณ„์‚ฐ + ๋น„๊ต ๋น„์šฉโ€์ด ๋ฆฌ์ปดํฌ์ง€์…˜์œผ๋กœ UI๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฆฌ๋Š” ๋น„์šฉ๋ณด๋‹ค ๋” ์ปค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4๏ธโƒฃ ๊ทธ๋ž˜์„œ ์–ธ์ œ derivedStateOf๊ฐ€ ํšจ๊ณผ์ ์ธ๊ฐ€?

  • When to Use derivedStateOf vs remember

โœ… ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์€ ์‹œ๋‚˜๋ฆฌ์˜ค

๊ณตํ†ต ์กฐ๊ฑด

  • State ๊ฐ€ ์ž์ฃผ ๋ณ€๊ฒฝ๋˜์ง€๋งŒ ์‹ค์ œ ๊ฐ’ ๋ณ€๊ฒฝ์€ ๋“œ๋ฌธ ๊ฒฝ์šฐ
  • derivedStateOfย ๋‚ด๋ถ€ ์—ฐ์‚ฐ ์ž์ฒด๊ฐ€ ๊ฐ€๋ฒผ์šด ๊ฒฝ์šฐ

๋Œ€ํ‘œ ์˜ˆ์‹œ: ์Šคํฌ๋กค ๊ด€๋ จ ์ฒ˜๋ฆฌ lazyListState

@Composable
fun ScrollToTopButton(lazyListState: LazyListState, threshold: Int) {
	  val isEnabled by remember(threshold) {
	      derivedStateOf { lazyListState.firstVisibleItemIndex > threshold }
    }
  
  	Button(onClick = { }, enabled = isEnabled) {
    	Text("Scroll to top")
  	}
}

์Šคํฌ๋กค ์ด๋ฒคํŠธ๋Š” ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๋ฐœ์ƒํ•˜์ง€๋งŒ,ย firstVisibleItemIndex > threshold์˜ ๊ฒฐ๊ณผ(Boolean)๋Š” ์ž„๊ณ„๊ฐ’์„ ๋„˜๋Š” ์ˆœ๊ฐ„์—๋งŒ ๋ฐ”๋€๋‹ˆ๋‹ค.ย derivedStateOf๊ฐ€ ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋ ˆ์ž„์—์„œ ๋ฆฌ์ปดํฌ์ง€์…˜์„ ๋ง‰์•„ ์ค๋‹ˆ๋‹ค.

์‚ฌ์šฉ ์˜ˆ์‹œ)

  • text ์œ ํšจ์„ฑ ๊ฒ€์ฆ
  • ํ•„ํ„ฐ๋ง๋œ ํ…์ŠคํŠธ ํ‘œ์‹œ (derivedStateOf { text.filter { it.isDigit() } })
  • LazyListState ์˜ ์Šคํฌ๋กค ์ƒํƒœ ์ฒ˜๋ฆฌ

5๏ธโƒฃ ์–ธ์ œ derivedStateOf๋ฅผ ์“ฐ๋ฉด ์•ˆ ๋˜๋Š”๊ฐ€?

๊ณ„์‚ฐ์„ ํ†ตํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ recomposition์„ ํ†ตํ•ด UI๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋น„์šฉ๋ณด๋‹ค ๋” ํฌ๋ฉด ์„ฑ๋Šฅ์— ์—ญํšจ๊ณผ๊ฐ€ ๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โŒ ํ”ผํ•ด์•ผ ํ•  ์‹œ๋‚˜๋ฆฌ์˜ค

1. ๊ฒฐ๊ณผ๊ฐ€ ๊ฑฐ์˜ ํ•ญ์ƒ ๋ฐ”๋€Œ๋Š” ๊ฒฝ์šฐ

@Composable
fun ScrollProgress(listState: LazyListState) {
    val progress by remember {
        derivedStateOf {
            val total = (listState.layoutInfo.totalItemsCount - 1).coerceAtLeast(1)
            listState.firstVisibleItemIndex / total.toFloat()
        }
    }

    LinearProgressIndicator(progress = progress)
}

ํ•ด๋‹น ์˜ˆ์‹œ๋Š” ์Šคํฌ๋กคํ•  ๋•Œ๋งˆ๋‹คย progressย ๊ฐ’์ด ๋ฐ”๋€๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๊ฐ€ ๊ฑฐ์˜ ํ•ญ์ƒ ๋‹ฌ๋ผ์ง€๋ฏ€๋กœย derivedStateOf์˜ ๋น„๊ต ๋น„์šฉ๋งŒ ์ถ”๊ฐ€๋  ๋ฟ ๋ฆฌ์ปดํฌ์ง€์…˜ ์Šคํ‚ต ํšจ๊ณผ๋Š” ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ๋Š” ๋‹จ์ˆœํžˆ recomposition์„ ํ—ˆ์šฉํ•˜๋Š” ํŽธ์ด ๋‚ซ์Šต๋‹ˆ๋‹ค.

2. ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋น„์‹ผ ๊ฒฝ์šฐ

  • filter/map/groupBy/sortedBy
  • ํ†ต๊ณ„ ๊ณ„์‚ฐ(sum/average) + ์ค‘๊ฐ„ ์ปฌ๋ ‰์…˜ ์ƒ์„ฑ
@Composable
fun GroupedSectionList(viewModel: MyViewModel) {
    val items by viewModel.items.collectAsState()     
    val query by viewModel.query.collectAsState()      

    val grouped by remember {                          
        derivedStateOf {
            items                                      
                .asSequence()
                .filter { it.name.contains(query, ignoreCase = true) }
                .groupBy { it.category }
                .mapValues { (_, v) -> v.sortedBy { it.date } }
                .toMap()
        }
    }
}

์ด๊ฒฝ์šฐ items๋‚˜ query๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค filter/groupBy/sortedBy ์ „์ฒด๊ฐ€ ์žฌ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ƒฅ ๋ฆฌ์ปดํฌ์ง€์…˜์„ ํ•˜๋Š” ๊ฒฝ์šฐ ๋ณด๋‹ค ๋” ํ•ด๋‹น ์‹์„ ์‹คํ–‰ํ•˜๊ณ  ๋น„๊ตํ•˜๋Š” ์—ฐ์‚ฐ์ด ๋” ๋น„์‹ธ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ๋‹จ์ˆœ ๊ณ„์‚ฐ

์•„๋ž˜ ๊ฐ™์€ ์—ฐ์‚ฐ์€ ๋น„์šฉ์ด ์‚ฌ์‹ค์ƒ 0์— ๊ฐ€๊น๊ณ , ๋ฆฌ์ปดํฌ์ง€์…˜ ๋น„์šฉ๋„ ๋Œ€๋ถ€๋ถ„ ๋งค์šฐ ๋‚ฎ์Šต๋‹ˆ๋‹ค.

derivedStateOf { list.isEmpty() }

์˜คํžˆ๋ ค ์ฝ”๋“œ ๋ณต์žก๋„๋ฅผ ์ฆ๊ฐ€ ์‹œํ‚ค๊ณ  ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ถ”๊ฐ€์ ์ธ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

6๏ธโƒฃ ๊ทธ๋Ÿผ ์ด๋Ÿฐ ๊ฒฝ์šฐ์—” ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜?

derivedStateOf ์•ˆ์—์„œ ์ •๋ ฌ/๊ทธ๋ฃนํ•‘ ๊ฐ™์€ ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด, UI ๋ ˆ์ด์–ด์—์„œ ๋ถˆํ•„์š”ํ•œ ์žฌ๊ณ„์‚ฐ์ด ๋ฐ˜๋ณต๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด๋Ÿด ๋•Œ๋Š” ๊ณ„์‚ฐ์„ Compose ๋ฐ–์œผ๋กœ ์˜ฎ๊ธฐ๊ณ , Compose๋Š” ๊ฒฐ๊ณผ๋งŒ ๊ทธ๋ฆฌ๋„๋ก ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์ด ๊ฐ€์žฅ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

  • ViewModel์—์„œ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐํ•ด UiState๋กœ ๋‚ด๋ ค์ฃผ๊ธฐ
  • Flow.map {} / StateFlow๋กœ ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ ๊ตฌ์„ฑ
  • ํ•„์š”ํ•œ ๊ฒฝ์šฐ Dispatchers.Default ๋“ฑ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌ
  • UI๋Š” โ€œ๊ฐ€๊ณต๋œ ๊ฒฐ๊ณผโ€๋งŒ ๋ฐ›์•„ ๊ทธ๋ฆผ
class MyViewModel(
    private val repository: Repository
) : ViewModel() {

    val uiState: StateFlow<UiState> =
        repository.itemsFlow
            .map { items ->
                UiState(
                    grouped = items.groupBy { it.category }
                )
            }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5_000),
                initialValue = UiState()
            )
}

data class UiState(
    val grouped: Map<Category, List<Item>> = emptyMap()
)

7๏ธโƒฃ ์‹ค์ „ ์งˆ๋ฌธ ์ •๋ฆฌ

Q) ์–ด๋–ค ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ derivedStateOf๊ฐ€ ํšจ๊ณผ์ ์ธ๊ฐ€์š”?

์—ฌ๋Ÿฌ State๋กœ๋ถ€ํ„ฐ ํŒŒ์ƒ๋œ ๊ฐ’์ด

์ž์ฃผ ์ฝํžˆ์ง€๋งŒ ์‹ค์ œ ๋ณ€๊ฒฝ์€ ๋“œ๋ฌธ ๊ฒฝ์šฐ์— ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค.

ํŠนํžˆ ์Šคํฌ๋กค ์ƒํƒœ๋กœ๋ถ€ํ„ฐ Boolean ๊ฐ’์„ ํŒŒ์ƒํ•˜๋Š” ๊ฒฝ์šฐ,

๋ถˆํ•„์š”ํ•œ recomposition์„ ์ค„์ด๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

Q) ์–ธ์ œ ์‚ฌ์šฉํ•˜์ง€ ๋ง์•„์•ผ ํ•˜๋‚˜์š”?

derivedStateOfย ์•ˆ์— ๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ์„ ๋„ฃ์œผ๋ฉด,

์˜์กด State๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์žฌ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋ˆ„์ ๋˜์–ด ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์— ์•…์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๊ฒฝ์šฐ ViewModel์ด๋‚˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ฒ˜๋ฆฌ๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

Q) remember ์—†์ด derivedStateOf๋งŒ ์“ฐ๋ฉด ์•ˆ ๋˜๋‚˜์š”?

์•ˆ ๋ฉ๋‹ˆ๋‹ค.ย remember๊ฐ€ ์—†์œผ๋ฉด ๋ฆฌ์ปดํฌ์ง€์…˜๋งˆ๋‹คย DerivedSnapshotStateย ๊ฐ์ฒด๊ฐ€ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์–ด ์บ์‹œ๊ฐ€ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

์ด์ „ ๊ฒฐ๊ณผ์™€ ๋น„๊ต ์ž์ฒด๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ด์ง€๋ฏ€๋กœย derivedStateOf์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.ย 

remember๋Š” ๊ฐ์ฒด๋ฅผ ์‚ด๋ ค๋‘๋Š” ์—ญํ• , derivedStateOf๋Š” ๊ทธ ๊ฐ์ฒด๊ฐ€ ์‚ด์•„์žˆ๋Š” ๋™์•ˆ ๋น„๊ตํ•˜๋Š” ์—ญํ• ๋กœ ๋‘˜์€ ๋ฐ˜๋“œ์‹œ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“•ย ์ฐธ๊ณ  ์ž๋ฃŒ

๋‹ค์Œ์˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ–ˆ์Šต๋‹ˆ๋‹ค.

profile
Seungjun Gong

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