이번 포스팅은 안드로이드개발하면서 한 번쯤은 들어보기도 했고 사용도 해본 Flow에 대해 알아보겠다!
# Flow란?
- 비동기 데이터를 처리할 때 사용하는 강력한 도구
- 시간이 지남에 따라 발생하는 데이터 스트림을 처리할 수 있게 해주는 개념
- RxJava의 Observable과 비슷하지만, Kotlin 코루틴을 기반으로 동작하므로 더 가볍고 직관적이다.
# 특징
- Cold Stream이다. 즉 구독(subscribe) 하기 전까지는 데이터를 생성하지 않는다. 데이터를 필요로 할 때만 흐름을 시작한다.
- map, filter, collect 등 다양한 연산자를 사용 가능하다.
# 구조
위 사진은 공식문서에서 나오는 사진이다. 사진을 보면 데이터 스트림에는 Consumer(소비자), Intermediary(중개자), Producer(생산자) 이렇게 3가지 항목이 있다. 여기서 Intermediary(중개자)는 선택사항이기 때문에 있어도 되고 없어도 된다.
이제 각각 항목을 알아보자!
# Producer (생산자)
- 데이터를 생성하고 Flow에 데이터를 내보내는 역할을 한다.
- 네트워크 요청, 데이터베이스 읽기, 센서 정보 등이 있다.
- 생산은 Flow 스코프 내에서 emit() 함수를 통해 데이터를 생산 후 방출 시킨다. (Flow 빌더는 코루틴 내에서 실행이 된다)
## 코드 예시
API를 통해 서버에 있는 데이터를 가져오는 예시이다.
interface ApiService {
suspend fun fetchData(): List<MyData>
}
class ApiDataSource(
private val apiService: ApiService,
) {
// flow 빌더 사용
suspend fun fetchData(): Flow<List<MyData>> = flow {
val response = apiService.fetchData()
// emit으로 생산한 데이터를 방출 시킨다.
emit(response)
}
}
코드에서 보다시피 Flow 빌더를 생성 후 값을 emit으로 생산해 방출을 시키고 있다.
# Intermediary (중개자) - 선택 사항
- 중간에서 데이터를 가공, 변환하거나 필터링하는 단계
- 예시로 네트워크에서 받은 데이터를 바로 사용하는 것이 아니라, UI에 맞게 변환하거나 필요한 부분만 선택해 처리할 수 있다.
## 코드 예시
생산자에서 생산한 데이터에서 데이터의 이름을 대문자로 변환하고 filter를 통해 빈 리스트가 아닌 경우에만 통과시키도록 한 중개자 예시 코드이다.
class ApiDataSource(
private val apiService: ApiService,
) {
// flow 빌더 사용
suspend fun fetchData(): Flow<List<MyData>> = flow {
val response = apiService.fetchData()
// emit으로 생산한 데이터를 방출 시킨다.
emit(response)
}
}
fun processData(apiDataSource: ApiDataSource): Flow<List<MyData>> {
return apiDataSource.fetchData()
.map { dataList ->
// 중개자: 데이터를 가공 (예: 각 데이터의 이름을 대문자로 변환)
dataList.map { data -> data.copy(name = data.name.uppercase()) }
}
.filter { dataList ->
// 중개자: 데이터를 필터링 (예: 빈 리스트가 아닌 경우에만 통과)
dataList.isNotEmpty()
}
}
flow의 다양한 연산자(map, filter)를 이용해 데이터를 가공, 필터링을 했다.이처럼 중간에 추가적인 처리를 할 수 있는 역할을 수행한다.
# Consumer (소비자)
- 최종적으로 데이터를 받아서 사용하는 역할
- collect를 통해 데이터 소비가 이루어진다.
collect 자세히 알아보기
- suspend 연산자이기 때문에 코루틴 내부에서 실행되어야 한다.
- 새로운 값이 생길 때마다 호출되는 함수를 파라미터로 받는다.
## 코드 예시
위에서 만든 processData함수에서 가공한 데이터를 collect를 통해 소비가 이루어지는 예시이다.
// 생산자
class ApiDataSource(
private val apiService: ApiService,
) {
// flow 빌더 사용
suspend fun fetchData(): Flow<List<MyData>> = flow {
val response = apiService.fetchData()
// emit으로 생산한 데이터를 방출 시킨다.
emit(response)
}
}
// 중개자
fun processData(apiDataSource: ApiDataSource): Flow<List<MyData>> {
return apiDataSource.fetchData()
.map { dataList ->
// 중개자: 데이터를 가공 (예: 각 데이터의 이름을 대문자로 변환)
dataList.map { data -> data.copy(name = data.name.uppercase()) }
}
.filter { dataList ->
// 중개자: 데이터를 필터링 (예: 빈 리스트가 아닌 경우에만 통과)
dataList.isNotEmpty()
}
}
// 소비자
suspend fun consumeData(apiDataSource: ApiDataSource) {
processData(apiDataSource).collect { dataList ->
// Consumer: 데이터를 받아서 처리 (예: UI에 출력)
println("Received data: $dataList")
}
}
이처럼 collect를 사용하면 LiveData의 subscribe처럼 사용이 가능하다!
이렇게 Flow의 구조와 사용법을 간단하게 알아봤다.
하지만 StateFlow나 SharedFlow 등 아직 Flow를 알기에는 한참 멀었다고 느꼈다.
다음 포스팅에는 더 심화로 한번 작성해볼까 한다.
https://velog.io/@ghddbwns9808/Android-Kotlin-Coroutine%EA%B3%BC-Flow-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B03-Flow-%EA%B0%9C%EB%85%90
https://developer.android.com/kotlin/flow?hl=ko
'Kotlin 공부 노트' 카테고리의 다른 글
[Kotlin Flow] StateFlow vs SharedFlow 예제와 함께 차이점 알아보기! (0) | 2024.11.30 |
---|---|
[Kotlin-Coroutine] coroutine Mutex(상호배제) 예제를 통한 사용법 (1) | 2024.06.09 |
[Android-Kotlin] Groovy DSL -> Kotlin DSL Migration(코틀린 DSL로 의존성 관리 마이그레이션), Kotlin DSL이란? (2) | 2023.10.29 |
DTO, DAO, VO에 알아보자! (0) | 2023.03.30 |
코틀린의 오브젝트란? (object, companion object) (0) | 2022.02.17 |