본문 바로가기
Kotlin 공부 노트

[Kotlin Flow] 코틀린 Flow란? - 예제와 함께 알아보기

by 지게요 2024. 9. 17.
728x90
반응형

이번 포스팅은 안드로이드개발하면서 한 번쯤은 들어보기도 했고 사용도 해본 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
반응형