collect를 사용한 데이터 소비의 한계점
Flow는 Coroutine상에서 Reactive한 프로그래밍을 할 수 있도록 만들어진 데이터 파이프 라인이다. Flow는 데이터를 발행하는 역할을 하며 Flow에서 발행하는 데이터는 collect의 action 파라미터에 의해 소비된다.
public suspend inline fun <T> Flow<T>.collect(crossinline action: suspend (value: T) -> Unit): Unit =
collect(object : FlowCollector<T> {
override suspend fun emit(value: T) = action(value)
})
collect의 인자로 들어가는 action 블록은 flow에서 발행된 데이터를 순차적으로 받아 suspend fun을 수행한다.
하지만 이 collect를 잘못 사용하면 새로운 데이터가 발행되더라도 데이터 처리가 제대로 되지 않을 수 있다.
예를 들어 flow에서 발행되는 특정 데이터가 처리하기 위해 많은 시간을 소모한다고 해보자. 그림1과 같이 데이터2가 많은 시간을 잡아먹을 경우 최신 데이터인 데이터3이 화면에 표기되는데 지연이 발생한다. 만약 중간에 있는 데이터가 처리되는데 무제한 시간을 잡아먹게 되면 이후 발행되는 데이터들은 모두 처리가 되지 않을 것임을 뜻한다.
이 문제는 Reactive Programming에서 매우 흔한 문제이며 flow 또한 다른 Reactive Programming Library들과 마찬가지로 이 문제를 처리하기 위해 다양한 해법을 제공한다.
위 문제를 처리하는 가장 간단한 방법은 그림2와 같이 최신 데이터가 들어왔을 때 이전 데이터를 이용해 수행하던 suspend fun을 취소하고 새로 들어온 데이터로 suspend fun을 수행하도록 만드는 것이다. 그렇게하면 최신 데이터가 들어왔을 때 최신 데이터를 처리한 결과만을 화면에 빠르게 표기할 수 있게 해준다.
바로 그림2의 동작이 collectLatest의 동작이다. 아래에서 collect와 collectLatest의 차이를 자세히 살펴보도록 하자.
Flow의 collect와 collectLatest 차이는 무엇인가?
Flow의 collect와 collectLatest의 차이는 새로운 데이터가 들어왔을 때의 처리 방식에 있다.
collect는 그림3과 같이 새로운 데이터가 들어왔을 때 이전 데이터의 처리가 끝난 후에 새로운 데이터를 처리한다.
이로 인해 collect로 데이터를 소비하게 되면 데이터가 발행 속도가 소비 속도보다 더 빠를경우 소비가 지연된다. 예를 들어 아래 그림4와 같이 0.1초마다 발행되어 0부터 10까지의 숫자가 1초만에 모두 발행이 끝났음에도 한 개를 처리하는데 1초씩 걸려서 10개를 모두 print 하는데는 10초나 걸리게 된다.
반면, collectLatest는 그림5와 같이 새로운 데이터가 들어오면 이전 데이터의 처리를 강제 종료시키고 새로운 데이터를 처리한다.
이러한 특성으로 인해 collectLatest는 항상 최신 데이터를 소비한다. 그림4의 코드를 collectLatest로 변경하면 collectLatest에서 delay를 주더라도 delay도중 취소되고 새로운 데이터로 발행되어 1초만에 모든 작업이 완료되는 것을 확인할 수 있다.
collectLatest는 최신 데이터를 이용해 UI를 구성하도록 하는 작업에 특화되어 있다. collect보다 collectLatest를 이용해 UI에 표기하는 것이 사용자에게 더욱 나은 사용자 경험을 만들어 줄 것이다.
collectLatest의 한계점
하지만 여전히 collectLatest에도 한계가 있다. 그림6에서는 데이터를 소비(print)한 후 delay를 걸었지만 실제 사용 시에는 delay가 일어난 후 소비되는 것이 일반적이다. 그림7과 같이 데이터 발행 시간 사이의 간격보다 데이터를 처리하는 suspend fun이 수행하는 시간이 오래 걸릴 경우 새로 들어온 데이터는 계속해서 취소되게 된다. 즉 이런 상황에서 collectLatest를 쓸 경우 중간 데이터를 하나도 얻지 못하고 마지막 데이터만을 얻을 수 있다.
그림6의 코드에서 delay의 위치를 위로 옮겨본 후 실행해보자. 그러면 중간 0부터 9는 소비되는 중간에 모두 취소되고 10만 소비되는 것을 확인할 수 있다.
flow는 이러한 상황에 대비하기 위해 다양한 옵션을 제공한다. 다음 글에서 flow의 다양한 옵션에 대해 알아보자.