최근 회사에서 안드로이드 개발을 하면서 Flow와 stateflow를 많이 사용하면서 개발을 진행하고있다.
오늘은 Flow와 Stateflow에 대해서 내가 알고있는것을 자세히 정리하려고한다.
기존에는 코틀린의 LiveData, RxJava, coroutine과 flow, stateflow 사용하기 전까지는 콜백과 리스너를 통해 이벤트 발생 시 특정 동작을 수행하는 역할을 개발해왔다 하지만 리스너를 사용하게 되면 코드가 복잡해지고 가독성이 떨어지며 메모리 누수와 같은 문제가 발생할 수 있었다
이후 LiveData , RxJava와 같은 반응형 프로그래밍 라이브러리들이 나오게 되었고 더 나아가 코루틴과 Flow가 나오게 되면서 이러한 문제들을 해결할 수 있었다. 최근 나같은 경우는 데이터의 변화에 따른 특정 동작을 수행할때는 모두 stateflow를 통해 개발을 진행해 오고 있다. 오늘은 flow에 대해서 자세히 정리할 예정이며 조만간 stateflow에 대해서도 정리할 예정이다.
Kotlin Flow란?
기존 명령형 프로그래밍에서는 데이터가 필요할 때마다 결과값을 매번 요청해야했었다는 점에서 비효율적이였다. 그러한 문제를 해결하기 위해 나온게 리액티브 프로그래밍이라는 건데 반응형 프로그래밍이다.
Flow가 바로 코루틴상에서 리액티브 프로그래밍을 지원하기 위한 구성요소 인 것이다.
리액티브 프로그래밍에서는 데이터를 만드는 생산자(발행자)가 존재하고 소비자는 데이터의 생산자(발행자)에게 그 구독을 요청하게 되면 데이터를 만드는 생산자(발행자)는 새로운 데이터가 생산or발행 되면 소비자에게 새로운 데이터를 지속적으로 발행을 한다.
즉 리액티브 프로그래밍에는 하나의 데이터를 발행하는 발행자가 있고, 해당 발행자는 데이터의 소비자에게 지속적으로 데이터를 전달하는 역할을 한다 - > 이것이 데이터 스트림 이다.
코루틴에서 이러한 데이터 스트림을 구현하기 위해서 Flow를 사용해야한다.
데이터 스트림의 구성요소 <-> Flow에서의 구성요소
1. 이벤트소스
- flowOf(): 주어진 값으로 Flow를 생성합니다.
- asFlow(): 기존의 컬렉션, 시퀀스, 배열 등의 데이터를 Flow로 변환합니다.
- callbackFlow(): 콜백을 사용하여 Flow를 생성합니다. 이 방법은 비동기적인 작업을 수행할 때 유용합니다.
2. 연산자
- map: 각각의 이벤트를 다른 이벤트로 변환합니다.
- filter: 주어진 조건에 맞지 않는 이벤트를 제거합니다.
- transform: 이벤트를 다른 Flow로 변환합니다.
- take: 주어진 수 만큼의 이벤트를 가져옵니다.
- zip: 두 개 이상의 Flow를 결합하여 새로운 Flow를 생성합니다.
- onEach: 각 이벤트에 대해 수행할 작업을 정의합니다.
3. 구독자
- collect: Flow의 모든 이벤트를 소비합니다.
- toList: Flow의 모든 이벤트를 리스트로 수집합니다.
- toSet: Flow의 모든 이벤트를 세트로 수집합니다.
- first: 첫 번째 이벤트를 반환합니다.
- reduce: Flow의 이벤트를 축소하여 하나의 결과를 생성합니다.
- fold: 초기값을 기반으로 Flow의 이벤트를 축소하여 하나의 결과를 생성합니다.
- catch: 예외를 처리하고 대체 값을 반환합니다.
- onCompletion: Flow의 실행이 완료되면 수행할 작업을 정의합니다.
Flow에 대한 간단한 예제
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking<Unit> {
val flow = flowOf(1, 2, 3, 4, 5)
.map { it + 1 }
flow.collect { println(it) }
}
위 예제를 보면 flowOf 함수를 사용하여 1부터 5까지의 정수형 데이터 스트림을 생성한다.
그리고 map 연산자를 사용하여 각 데이터에 1을 더해주고 마지막으로 collect 함수를 사용하여 각각의 데이터를 출력한다.
flow는 이런식으로 생성하고 사용할 수 있다.
flow를 정리하자면 비동기적으로 데이터 스트림을 생성 및 처리, 변환 할 수 있는 역할을 한다. 또한 Flow는 코루틴의 suspend함수로 구현되어 있기 때문에 비동기 프로그래밍을 더욱 쉽게 구현 할 수 있도록 도와준다.
Flow를 사용해야 하는 이유
1. 비동기 데이터 스트림 처리
Flow는 비동기 데이터 스트림을 생성하고 처리할 수 있어, 대량의 데이터를 처리할 때 유용합니다. 또한, 데이터가 처리되기 전까지 suspend 상태를 유지하므로 메모리를 효율적으로 사용할 수 있습니다.
2. 코루틴과의 통합
Flow는 코루틴과 함께 사용할 수 있어, 비동기 처리를 보다 쉽게 구현할 수 있습니다. 코루틴의 suspend 함수로 구현되어 있으므로, 코루틴의 동시성과 비동기성을 보다 쉽게 처리할 수 있습니다.
3. Cold Stream 지원
Flow는 Cold Stream으로 구현되어 있어, 데이터 스트림을 처리하기 위해 collect() 함수를 사용해야 합니다. 이를 통해 데이터 스트림을 처음부터 다시 생성하고 처리할 수 있어, 상황에 맞게 데이터 스트림을 적절히 처리할 수 있습니다.
4. 연산자 지원
Flow는 연산자(operator)를 지원하여, 데이터를 변환하고 처리할 수 있습니다. 연산자를 사용하면 데이터 스트림을 보다 쉽게 처리할 수 있어, 코드의 가독성과 유지보수성을 높일 수 있습니다.
5. Backpressure 처리
Flow는 backpressure 처리를 지원하므로, 데이터의 생산 속도와 소비 속도가 일치하지 않을 때도 안정적으로 동작할 수 있습니다.
따라서, Flow는 안드로이드에서 비동기 처리를 보다 쉽게 구현하고, 안정적으로 처리할 수 있도록 도와주는 유용한 기능입니다.
'안드로이드(Kotlin)' 카테고리의 다른 글
Kotlin 코틀린의 DI(의존성 주입) (0) | 2023.03.26 |
---|---|
Kotlin 코틀린의 Sealed Class (0) | 2023.03.17 |
Kotlin 코틀린의 Coroutine (1) | 2023.02.09 |
Kotlin 코틀린의 object VS companion object (0) | 2022.08.30 |
Kotlin 코틀린의 싱글톤(Singleton) (0) | 2022.08.30 |