[Android/Kotlin] Flow 결합 연산자 (zip, combine)

minH_·2024년 7월 5일

다음과 같은 2개의 Flow를 결합하고자 한다.

 val flow1 = flowOf(1, 2, 3).onEach { delay(10L) }
 val flow2 = flowOf("A", "B", "C", "D").onEach { delay(15L) }

zip

zip 연산자는 2개의 flow를 결합하여 하나의 flow로 흘려보내 주는 함수이다.
먼저 zip 함수의 결과를 보자.

flow1.zip(flow2) { num, str ->
    "$num $str"
}.collect {
    Log.d(TAG, it)
}

위 코드의 결과는 다음과 같다.

분명 flow2는 A, B, C, D의 데이터 스트림인데 D는 어디갔을까?

먼저 zip 함수는 2개의 flow만 결합 가능하다. (아래에서 설명할 combine과의 차이점)
또한 flow1에서 데이터를 방출했어도, flow2에서 데이터를 방출하지 않으면 방출할 때 까지 기다렸다가 flow2에서 데이터를 방출할 때 두 flow를 결합하여 하나의 flow로 방출한다. 그리고 둘 중 하나의 flow에서 방출이 완료되면 그 즉시 종료된다.

combine

combine 연산자는 2개 이상의 flow를 결합하여 하나의 flow로 흘려보내 주는 함수이다.
똑같이 먼저 결과를 보자.

flow1.combine(flow2) { num, str ->
    "$num $str"
}.collect {
     Log.d(TAG, it)
}

zip과 상당히 다른 결과가 나왔다. 그 이유는 무엇일까?

combine 함수는 2개 이상의 flow를 결합할 수 있다. 초기에 flow1에서 데이터를 방출하고, flow2에서 데이터를 방출할 때 까지 기다리는건 똑같다. (최초 한 쌍의 flow가 방출되어야 함) 하지만 flow2에서 데이터를 방출하고 다시 flow1에서 '2'를 방출하였을 때, 아직 flow2는 "B"를 방출하지 않았다. "B"를 방출하지 않았지만, 2는 A와 결합되어 방출된 결과를 볼 수 있다. 다시 말해서 가장 최근에 방출된 데이터와 결합된다는 뜻이다. (하나의 flow에서 방출이 완료되어도 결합이 종료되지 않음)

만약 3개의 flow를 결합했다고 가정해보자.
3개의 flow 중 하나의 flow에서만 값이 방출되면 나머지 두 개의 flow에서 가장 최근에 방출된 데이터와 결합되어 하나의 flow로 방출된다. 필자는 검색 기능을 구현할 때 combine을 유용하게 사용하고 있다. 아래는 간단한 코드 예시이다.

    private val questionFilterFlow = combine(
        schoolFilterFlow,
        categoryQueryFlow,
        searchQueryFlow.debounce(300L)
    ) { schoolFilter, category, searchKeyWord ->
        Triple(schoolFilter, category, searchKeyWord)
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    fun getQuestions() =
        questionFilterFlow.flatMapLatest { (schoolFilter, category, searchKeyWord) ->
            Pager(
                config = PagingConfig(pageSize = 20),
                pagingSourceFactory = {
                    QuestionPagingSource(
                        questionRepository,
                        schoolFilter = schoolFilter,
                        boardCategory = category,
                        searchKeyWord = searchKeyWord
                    )
                })
                .flow
                .cachedIn(viewModelScope)
        }

검색어, 우리학교 여부, 단과대학 필터링 flow 중 하나의 flow에서 새로운 값(변경 값)이 방출되면, 나머지 두 개의 검색 필터링은 유효한 채로 해당 필터만 변경할 수 있다.
(위 코드에서 combine 한 Flow를 flatten 한 이유는 combine은 최신 값을 방출하지만, 최소 한 쌍이 방출되지 않으면 합쳐서 방출이 되지 않음, ( ex -> 사용자가 검색어만 입력한 경우) 또한 둘 중 하나라도 변경되면 새로운 게시글을 가져와야 하기 때문)


틀린 부분이 있을 수 있습니다. 댓글 언제나 환영입니다.

참고 자료:
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/zip.html
https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/combine.html

0개의 댓글