본문 바로가기
Kotlin

[Kotlin] StateFlow: Android 상태 관리를 위한 필수 도구

by LoseyKim 2024. 11. 15.

오늘은 Kotlin의 `kotlinx.coroutines.flow`에서 제공하는 강력한 상태 관리 도구인 StateFlow에 대해 알아보려고 해요. 이 글을 통해 `StateFlow`의 개념부터 실제 활용 사례까지 자세히 살펴볼게요.


1. StateFlow란 무엇인가요?

`StateFlow`는 `kotlinx.coroutines.flow`에서 제공하는 특별한 종류의 `Flow`로, 상태 관리에 최적화된 Hot Stream이에요. 항상 현재 상태를 보유하며, 상태가 변경될 때 구독자들에게 이를 알리는 데 사용돼요.

특히 Jetpack Compose와 같이 상태 기반의 UI 프레임워크에서 유용하게 활용할 수 있어요. 뷰모델에서 상태를 관리하고, UI는 이를 구독해서 자동으로 상태를 반영하도록 설계할 때 적합해요.


2. StateFlow의 특징

  1. 항상 현재 값을 유지
    `StateFlow`는 항상 최신 상태 값을 보유하고 있어요. 구독자가 구독을 시작하면 가장 최신 값을 즉시 받을 수 있어요.
  2. Hot Stream
    `StateFlow`는 구독자가 없어도 최신 값을 유지해요. 이는 일반 `Flow`(Cold Stream)와의 주요 차이점이에요.
  3. 중복 방지
    동일한 값으로 상태를 변경하려고 해도 구독자들에게 이벤트가 전달되지 않아요. 이는 불필요한 상태 업데이트를 방지하는 데 유용해요.
  4. `MutableStateFlow`를 통한 상태 변경
    `StateFlow`는 읽기 전용으로 제공돼요. 상태를 변경하려면 `MutableStateFlow`를 사용해야 해요.
  5. UI와의 강력한 연동
    Jetpack Compose의 `collectAsState()` 함수와 결합하면 매우 간단하게 상태 기반 UI를 구축할 수 있어요.

3. StateFlow의 기본 사용법

1. 선언과 초기화

// ViewModel 내부
private val _state = MutableStateFlow(0) // 초기값 설정
val state: StateFlow<Int> = _state // 외부에는 읽기 전용 StateFlow 제공

2. 상태 변경

_state.value = 1 // 값을 업데이트
_state.value = 2 // 또 다른 상태값

3. 상태 구독

lifecycleScope.launch {
    viewModel.state.collect { value ->
        // 상태값을 UI에 반영
        textView.text = value.toString()
    }
}

4. StateFlow와 LiveData의 차이

특성 StateFlow LiveData
코루틴 지원 코루틴 기반으로 설계됨 코루틴과의 통합이 제한적
백프레셔 처리 항상 최신 상태만 유지 백프레셔 관리 필요 없음
구독 상태 최소 1개의 활성 구독자 필요 구독자가 없어도 상태 유지 가능
Thread Safety 완전히 코루틴 기반으로 안전 UI Thread에서만 동작하도록 설계됨

5. StateFlow의 활용 예시

1. ViewModel에서 상태 관리

뷰모델에서 상태를 관리할 때 `StateFlow`를 사용하면 UI와 상태를 쉽게 동기화할 수 있어요.

class MyViewModel : ViewModel() {
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    val uiState: StateFlow<UiState> = _uiState

    fun fetchData() {
        viewModelScope.launch {
            try {
                val data = repository.getData()
                _uiState.value = UiState.Success(data)
            } catch (e: Exception) {
                _uiState.value = UiState.Error("데이터 로드 실패")
            }
        }
    }
}

sealed class UiState {
    object Loading : UiState()
    data class Success(val data: List<String>) : UiState()
    data class Error(val message: String) : UiState()
}

2. UI에서 상태 구독 (Jetpack Compose)

Jetpack Compose에서 상태를 구독해 UI를 업데이트하는 방법이에요.

@Composable
fun MyScreen(viewModel: MyViewModel) {
    val uiState by viewModel.uiState.collectAsState()

    when (uiState) {
        is UiState.Loading -> Text("로딩 중...")
        is UiState.Success -> LazyColumn {
            items((uiState as UiState.Success).data) { item ->
                Text(item)
            }
        }
        is UiState.Error -> Text("에러: ${(uiState as UiState.Error).message}")
    }
}

참고자료

https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/

StateFlow

A SharedFlow that represents a read-only state with a single updatable data value that emits updates to the value to its collectors. A state flow is a hot flow because its active instance exists independently of the presence of collectors. Its current valu

kotlinlang.org