Coroutines/Flow

[Coroutine Flow] 3. stateIn 사용하여 Flow를 StateFlow로 변환하기

반응형

Flow와 StateFlow

우리는 리액티브 프로그래밍을 할 때 여러 데이터 흐름(flow)를 하나로 합쳐 하나의 데이터 흐름(Flow)으로 만들어낸다. 예를 들어 아래의 그림3에서는 Flow가 3개 있고 이것이 합쳐져 하나의 Flow를 만들어낸다.

이러한 상황은 흔하다. 예를 들어 우리가 영화 평점 앱을 만든다고 할 경우 영화에 대한 정보, 사용자에 대한 정보, 사용자의 영화의 평점에 대한 정보를 각 테이블에서 가져와 하나의 객체로 만들어야 한다.

그림1. Flow와 StateFlow

하나로 만들어진 Flow는 UI에서 사용되기 위해 StateFlow로 변환되어야 한다. 이 UI에서는 이 StateFlow를 구독하여 항상 최신 데이터를 발행받는다. 

 

이것이 가능하기 위해서는 Flow를 StateFlow로 변환하는 로직이 필요하다. 또한 StateFlow가 항상 Flow를 구독하고 있으면 메모리 누수가 생기므로 이 StateFlow가 살아있어야 하는 CoroutineScope을 명시할 수 있어야 한다. 우리는 이를 stateIn 함수를 통해 할 수 있다.

 

*StateFlow는 Cold Stream 이 아니라 Hot Stream이다. 마지막 홀딩하고 있는 데이터를 구독하는 구독자에게 전달할 뿐, 구독자가 구독할 때 발행을 위한 로직을 Trigger 하지는 않는다.

 

 

stateIn 사용하여 Flow를 StateFlow로 변환하기

stateIn 함수를 사용하면 Flow를 StateFlow로 변환할 수 있다. stateIn내부는 다음과 같다.

public fun <T> Flow<T>.stateIn(
    scope: CoroutineScope,
    started: SharingStarted,
    initialValue: T
): StateFlow<T> {
    val config = configureSharing(1)
    val state = MutableStateFlow(initialValue)
    val job = scope.launchSharing(config.context, config.upstream, state, started, initialValue)
    return ReadonlyStateFlow(state, job)
}

stateIn은 세가지 변수를 받는다.

  • scope : StateFlow가 Flow로부터 데이터를 구독받을 CoroutineScope을 명시한다.
  • started : Flow로부터 언제부터 구독을 할지 명시할 수 있다.
  • initialValue : StateFlow에 저장될 초기값을 설정한다.

 

 

이 3가지 변수를 이용한 예제를 하나를 만들어보자. 1초마다 String 값을 발행하는 간단한 Flow를 다음과 같이 선언한다.

val stringFlow: Flow<String> = flow {
    for (i in 0..1000) {
        emit("integer: $i")
        delay(1000)
    }
}

 

이 flow를 stateIn함수를 이용하여 StateFlow로 변환한다.

  • 변수의 시작 시 저장 값은 "integer 0"이여야 한다. 따라서 initialValue를 0으로 한다.
  • started의 WhileSubscribed는 collector 가 없어졌을 때 지정된 시간 이후 StateFlow 발행을 멈추도록 만드는 값이다. 따라서 collector가 없어진 후 5초 후에 동작을 멈추도록 만들기 위해 SharingStarted.WhileSubscribed(5000)을 설정한다.
  • 이 StateFlow가 Flow로부터 구독을 하는 범위는 ViewModel이 살아있을 때까지만이다. 따라서 viewModelScope을 scope(구독 범위)로 넘긴다.
val stateFlow = stringFlow.stateIn(
    initialValue = "integer 0",
    started = SharingStarted.WhileSubscribed(5000),
    scope = viewModelScope
)

이렇게 하면 초기 저장값은 "integer 0"이고, 구독 후 5초 후에 처음 발행 받고, ViewModel의 생명주기만큼만 구독받는 행동을 하는 StateFlow가 만들어진다.

 

 

반응형

 

이 글의 저작권은 Kotlin World 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.