본문 바로가기
Android Jetpack Compose/Jetpack Compose

mutableStateOf 와 MutableStateFlow 비교 총정리 # collectAsState

by Developer88 2023. 4. 23.
반응형

Jetpack Compose UI의 중심에 있는 Concept가 State 인데요.

mutableStateOf 와 MutableStateFlow를 이용하면,

mutable (가변)의 State 값을,

관찰해서 값의 변화에 따라 UI 나 혹은 다른 로직의 변형을 줄 수 있습니다.

오늘은 이 둘의 차이에 관해서 정리해 보겠습니다.

 

1. mutableStateOf

mutableStateOf에 관해서는 공식문서에 자세히 설명이 되어있는데요.

관찰될 수 있는 MutableState<T>를 생성해주는데,

이 타입으로 된 값이 변경될 때마다,

컴포저블이 다시 recompose 되도록 한다고 되어있습니다.

즉, 값에 따라서 Composable UI의 변경을 주어야 할 때 사용하기에 적합합니다.

 

 

사용방법은 아래와 같은데요.

위의 공식문서 설명에도 3가지 선언방법이 있지만, by키워드를 이용하는 것이 가장 심플해 보입니다.

 

Column {
    var name by remember { mutableStateOf("") }
    if (name.isNotBlank()) {
        Text(
            text = "Hello, $name!",
        )
    }
}

 

 

참고로 by키워드에 대해서는 아래의 글을 참조해 주세요.

>> Kotlin By 키워드에 대한 이해 # Property Delegate Pattern

 

무엇보다도 아래에서 볼 MutableStateFlow와 다르게,

androidx.compose.runtime 즉 안드로이드의 Jetpack Compose UI를 위한 API에서 동작하는 API입니다.

좀 더 UI에 특화되어 있다고 볼 수 있겠지요.

 

 

2. MutableStateFlow

2-1. StateFlow 와 MutableStateFlow  

MutableStateFlow는 위에서 본 jetpack Compose UI를 위해서 사용하는 mutableStateOf() API와는 다르게,

Kotlin의 Coroutine 에 존재하는 Flow 의 API 입니다.

즉, Coroutine의 Flow를 사용하기 위한 것이므로, 

위에서와 같이 값이 변하더라도 UI가 recompose 되거나 하는 일은 없습니다.

UI와 관련이 없는 과업에 사용하면 되겠지요.

 

StateFlow는 MutableStateFlow의 read-only 타입의 부모로서,

state flow 값을 생성하거나 변경할 때 사용하는 것이 MutableStateFlow 입니다.

 

StateFlow는 업데이트가 가능한 데이터값을 가지는 State라는 것을 가지고,

collector에게 emit(전파)하는 인터페이스입니다.

State를 가지고 있다가, 변경되면 emit을 해주는 역할을 하는 핫스트림입니다.

 

핫스트림과 콜드스트림에 대해서는 아래 글을 참조해 주세요.

>> Kotlin Coroutine Flow 총정리 part3 # launchIn

 

여러 Collector들에 의해서 값이 관찰될 수 있구요.

StateFlow는 Complete이라는 개념이 없이 계속해서 관찰될 수 있습니다.

 

 

2-2. MutableStateFlow 사용방법

사용시에는 MutableStateFlow타입의 변수를 바로 Collector 들에게 노출시기기 보다는,

아래와 같이, 앞에 "_"를 붙여서 MutableStateFlow를 초기화 값과 함께 선언하고,

외부에 노출시킬 변수를 별도로 "_"없이 만들어서,

기존의 MutableStateFlow타입의 변수에 "asStateFlow()"함수를 이용해,

read-only인 StateFlow 타입으로 선언해 캡슐화 해서 사용하게 됩니다.

 

class MyCounter {
    private val _counter = MutableStateFlow(0)
    val counter: StateFlow<Int> = _counter.asStateFlow()
    suspend fun incrementCounter() {
        _counter.value++
    }
}

 

 

2-3. collectAsState()

그런데, Flow타입을 Jetpack Compose의 State로 변경해주는 함수가 있습니다.

그럼 위에서 mutableStateOf()를 썻을 때처럼,

Flow타입의 데이터가 변화될 때 JetpackCompose UI 에서 recompose 되게 할 수 있습니다.

 

val count by viewModel.counter.collectAsState()

Button(onClick = { viewModel.incrementCounter() }) {
    Text("$count times 번 클릭함")
}

 

3. mutableStateOf vs collectAsState

3-1. 비교

둘이 비슷하게 사용할 수 있는 것이 아닐까 라고 생각이 들 수 있습니다.

결국 둘다 State을 다루게 해주고, 그에 따라 Jetpack Compose의 UI를 변경하게 도와주니까요.

 

API사용 측면에서 둘을 구분해 보면 다음과 같습니다.

UI가 데이터에 따라 변동되는 것에서는 비슷하구요.

그 데이터의 소스가 Flow이어야 하는지, 아닌지에 따라 차이가 있습니다.

 

구분 mutableStateOf collectAsState
용도 Jetpack Compose UI내에서,
변경되는 State객체의 값에 따라,
UI를 변경하고자 할 때 사용
Composable 함수에서,
Flow가 emit(방출)해주는 데이터의 강물을 관찰하여서,
값에 따라서 UI를 변경할 때 사용

 

 

3-2. 실제 사례 비교

mutableStateOf 와 CollectAsState를 실제로 사용하는 코드에서 비교해 보도록 하겠습니다.

구현하는 코드는 유저가 무언가를 입력하면, 데이터베이스에서 return 해주는 flow타입의 데이터를 받는 것인데요.

items를 MutableStateFlow 또는 mutableStateOf로 구현 하고,

유저가 입력시에 searchItemsByName 함수를 실행해서 items에 데이터가 흘러가도록 하겠습니다.

 

A. MutableStateFlow 구현

먼저 MutableStateFlow로 구현한 것을 보도록 하겠습니다.

 

private val _items = MutableStateFlow<List<ItemEntity>>(emptyList())
val items: StateFlow<List<ItemEntity>> = _item.asStateFlow()

private suspend fun searchItemsByName(name: String) {
    itemRepository.searchItemsByName(name).collect { result ->
        _items.value = result
    }
}

 

 

B. mutableStateOf

이번에는 mutableStateOf 로 변환해 보도록 하겠습니다.

set을 임의로 해야하는 이런 케이스에서는 by키워드 사용을 하게되면,

private하게 items를 할 수 없으므로 여기서는 사용하지 않았습니다.

이렇게 될 경우는 mutableStateOf 라고 크게 코드가 간결해지지는 않습니다.

 

private val _items = mutableStateOf<List<ItemEntity>>(emptyList())
val items: State<List<ItemEntity>> = _items

private suspend fun searchItemsByName(name: String) {
    ItemRepository.searchItemsByName(name).collect { result ->
        _items.value = result
    }
}

 

4. 정리

Flow 처럼 data stream처리에 필요한 API를,

앱 사용중 아주 단순하게 몇번 바뀌는 값에 사용하는 것은,

소잡는 칼로 닭잡는 모양이 될 것 같습니다.

앱시작시 intialize 되거나 단순한 토글등의 이벤트에 가끔 변경되는 값이라면, mutableStateOf로 충분하겠지요.

 

Flow API의 MutableStateFlow 와 mutableStateOf를 적절하게 잘 사용하는 것이,

효율적으로 API를 사용하는 것이라고 할 수 있겠습니다.

728x90

댓글