Android, Flow
Android에서는 꽤 오래전부터 rxJava 와 함께 LiveData를 주로 사용해왔다.
그리고 rxJava에서 Coroutine으로 Migration을 진행하며 점차 LiveData보다는 Flow를 더 많이 사용하는 추세로 변해가고 있다.
그렇다면, 우리가 주로 사용하는 Flow는 무엇인지와 종류, 특징, 그리고 어떻게 사용하는지에 대하여 간략하게 적어보려 한다.
또한 추가적으로 LiveData와의 간단한 비교도 함께 해볼 예정이다.
1. Flow 란?
Flow를 개발한 Kotlin 공식 홈페이지에서 Flow를 찾아보면 아래와 같이 기술되어있다.
An asynchronous data stream that sequentially emits values and completes normally or with an exception. Intermediate operators on the flow such as map, filter, take, zip, etc are functions that are applied to the upstream flow or flows and return a downstream flow where further operators can be applied to.
위 구문을 직역해보면 순차적으로 값을 방출하고 정상적으로 또는 예외와 함께 완료되는 비동기 데이터 스트림으로 map, filter, take, zip 등의 중간 연산자는 upstream flow에 적용되고 downstream을 반환하는 함수라고 설명하고 있다.
조금 더 풀어서 본다면, 우리가 정의한 데이터 스트림에 중간 연산자를 사용하여 스트림을 변환하고, 그에 대한 결과를 반환한다고 볼 수 있을 것이다.
아래의 코드블럭으로 간단한 예시를 들어보자.
(0..10).asFlow().map {
it * 10
}.collect {
println(it)
}
위의 코드블럭을 간단히 분석해보면 (0..10).asFlow()를 통해서 0부터 10까지의 정수형 값을 가지고 있는 Flow
위 코드를 실제로 돌려보면 아래와 같이 값을 방출하게 된다.
콘솔에 출력된 값을 보게되면, 우리는 0..10이라는 데이터를 넣었지만 중간 연산자를 통해서 * 10이 적용 된 결과값이 방출된 것을 알 수 있다.
이와 같은 데이터 stream 을 Flow라고 한다.
또한, 흐름을 수집하는 행위는 비동기로 이루어지기 때문에 suspend fun 내에서만 가능하다.
2. Stream의 종류 및 특징
StateFlow와 SharedFlow를 알아보기에 앞서, Flow에서 Stream이 무엇이며, 종류는 어떻게 되는지, 각 특징은 무엇인지를 먼저 보도록 하자.
1) Flow에서 Stream이란?
Stream이란 사전적용어로는 ‘줄줄 흐르다’, ‘줄을 지어 이어지다’ 라는 의미를 갖고있다.
: 앞서 보았듯이 Flow는 Data Stream으로 정의되었다. 그렇다면 Flow에서의 Stream이란 Data가 갖는 연속적인 흐름을 의미한다고 볼 수 있다.
또한 Stream은 사용에 따라 Hot/Cold의 두 가지로 구분된다.
2) Cold Stream
- 하나의 Stream에 구독자 별 Stream이 생성 및 시작되어 데이터를 전달하며, 구독이 이루어지지 않으면 어떠한 연산도, 방출도 이루어지지 않는다.
- 기본적으로 Flow Builder를 통해 생성한 flow들이 Cold Stream에 속한다.
3) Hot Stream
- Stream이 생성되는 시점부터 바로 연산 및 데이터 전달이 시작되며, 다수의 구독자가 동일한 Stream에서 데이터를 전달
- StateFlow, SharedFlow 등이 Hot Stream에 속한다.
결론적으로, Cold / Hot Stream의 차이는 여러 구독자가 하나의 Stream에서 소비를 하는지, 개별 Stream에서 소비를 하는지 일 것이다.
3. StateFlow와 SharedFlow 특징 및 간단한 사용법
1) StateFlow
- 초기값이 필요하다.
- 하나의 값만 가질 수 있으며, 항상 최신 값만 받을 수 있다. (마지막 값만 보유, 기존 값 보유 불가)
- 동일한 값은 방출하지 않는다.
2) SharedFlow
- 초기값 불필요
- replay를 통해 신규 구독자에게 몇개의 지난 값을 emit 할지 정의할 수 있다.
- sharedFlow가 0..10까지의 값을 방출하던 중 4부터 구독을 시작했다고 하면, replay 값이 1이면 3,4,5…가 방출되고, 2이면 2,3,4,5…가 방출된다.
- emit 속도가 collect 속도보다 빠른 경우, extraBufferCapacity를 통해 아직 소비하지 못한 값을 저장할 수 있다.
- onBufferOverflow를 통해 버퍼가 가득 찼을 경우의 처리방법을 정의할 수 있다.
위의 내용은 아래 Sample을 통해 알아볼 수 있겠다.
결과에서 보는바와 같이, stateFlow는 같은값을 최초에 1회만 방출하고, sharedFlow는 같은 값을 계속하여 방출하는 것을 알 수 있다.
4. LiveData vs Flow
LiveData는 기본적으로 Android 플랫폼에 대한 의존성이 있어야 사용 가능하다.
하지만 Clean-Architecture의 Domain Module은 Android 의존성이 없는 순수 Java/Kotlin 코드로 이루어 져야 한다고 얘기하고 있다.
따라서 LiveData는 Clean-Architecture의 DomainModule에서는 사용이 불가하다는 결론이 나온다.
반면, Flow는 Android의 의존성이 없이 사용이 가능하기 때문에 Domain Module에서도 제약없이 사용할 수 있기 때문에 사용하는 이점도 크다.
마지막으로, Flow.asLiveData, LiveData.asFlow를 사용하여 LiveData와 Flow를 혼용하여 사용도 가능하다.
5. Finally
마지막으로 실제 사용 경험을 기술하며 마무리 하려한다.
필자는 프로젝트 내 모든 LiveData를 Flow로 마이그레이션 하여 사용중인데, 일각에서는 Presentation Module에서 Livedata를, 그 외에는 Flow를 사용하는 경우도 왕왕 보이긴 한다.
무엇이 더 낫다고 얘기하기도 힘들 뿐 아니라, 옳고 그름을 논하기도 어렵다고 생각한다.
다만, 필자의 경우에는 가급적 Data Stream을 하나로 일관되게 사용하고 싶었고, DomainModule에서 Android 의존성을 배제해야 하기 때문에 Flow로 통일하여 사용하였다고 보면 될 것이다.
댓글남기기