티스토리 뷰
728x90
플로우(Flow)는 데이터 스트림을 비동기적으로 처리하기 위한 강력한 도구입니다. 플로우 연산자는 플로우 내에서 데이터를 변환하거나 처리할 때 사용하는 중간 연산자(intermediate operators)와 최종 연산자(terminal operators)로 나뉩니다. 플로우 연산자를 통해 데이터를 필터링, 변형, 조합하거나 오류를 처리하는 등 다양한 작업을 수행할 수 있습니다.
1. 플로우의 구조
- 중간 연산자: 플로우 내 데이터를 변형하거나 필터링하는 역할을 하며, 여러 번 사용할 수 있습니다. 플로우 자체를 변경하지 않고, 콜드 스트림을 유지합니다.
- 최종 연산자: 플로우에서 데이터를 수집하는 역할을 하며, 데이터의 처리가 이 연산자를 통해서 완료됩니다.
collect
가 대표적입니다.
2. 중간 연산자 (Intermediate Operators)
2.1 map
map
은 플로우 내 데이터를 변환하는 연산자입니다. 각 요소에 대해 변형 작업을 수행하고, 변형된 데이터를 다시 방출합니다.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
(1..5).asFlow()
.map { it * 2 } // 각 요소를 두 배로 변환
.collect { value -> println(value) }
}
설명:
map
을 사용하여 플로우의 모든 요소를 변환합니다. 위 예제에서는 각 요소가 두 배로 변환되어 출력됩니다.
출력:
2
4
6
8
10
2.2 filter
filter
는 플로우 내 요소 중 특정 조건을 만족하는 요소만 통과시키는 연산자입니다.
fun main() = runBlocking {
(1..10).asFlow()
.filter { it % 2 == 0 } // 짝수만 통과
.collect { value -> println(value) }
}
설명:
filter
는 각 요소를 필터링하여, 짝수만 통과시킵니다.
출력:
2
4
6
8
10
2.3 take
take
는 플로우에서 지정한 개수만큼 데이터를 방출하고, 그 이후의 데이터는 무시합니다.
fun main() = runBlocking {
(1..10).asFlow()
.take(3) // 처음 3개의 요소만 수집
.collect { value -> println(value) }
}
설명:
take
는 최대 3개의 요소만 방출하며, 그 이후의 요소는 무시됩니다.
출력:
1
2
3
2.4 transform
transform
은 플로우 내 데이터를 더 유연하게 변형할 수 있게 해주는 연산자입니다. 단순한 변형뿐만 아니라, 여러 요소를 방출하거나 추가적인 비동기 작업을 수행할 수 있습니다.
fun main() = runBlocking {
(1..5).asFlow()
.transform { value ->
emit(value * 2) // 두 배로 변환
emit(value * 3) // 세 배로 변환
}
.collect { value -> println(value) }
}
설명:
transform
은 각 요소를 두 번 방출합니다. 하나는 두 배로, 다른 하나는 세 배로 변형된 값입니다.
출력:
2
3
4
6
6
9
8
12
10
15
2.5 flatMapConcat
, flatMapMerge
, flatMapLatest
이 연산자들은 각각 다른 플로우를 연결하는 역할을 하지만, 처리 방식이 다릅니다.
flatMapConcat
: 순차적으로 플로우를 연결하여 처리합니다.flatMapMerge
: 병렬로 여러 플로우를 연결하여 처리합니다.flatMapLatest
: 새로운 플로우가 생성되면 이전 플로우를 취소하고 최신 플로우만 처리합니다.
예시: flatMapConcat
fun main() = runBlocking {
(1..3).asFlow()
.flatMapConcat { number ->
flow {
emit(number * 2)
delay(500L) // 각 플로우의 지연
emit(number * 3)
}
}
.collect { value -> println(value) }
}
설명:
flatMapConcat
은 각 플로우가 순차적으로 완료된 후, 다음 플로우를 실행합니다.
출력:
2
3
4
6
6
9
3. 최종 연산자 (Terminal Operators)
3.1 collect
collect
는 플로우에서 데이터를 수집하는 최종 연산자입니다. Flow의 데이터 처리는collect()
를 호출할 때 시작됩니다.
fun main() = runBlocking {
(1..3).asFlow()
.collect { value -> println("Collected $value") }
}
설명:
collect
는 플로우의 데이터를 최종적으로 수집하며, 수집된 데이터를 출력합니다.
출력:
Collected 1
Collected 2
Collected 3
3.2 reduce
reduce
는 플로우의 모든 데이터를 순차적으로 누적하여 최종 값을 도출하는 연산자입니다.
fun main() = runBlocking {
val sum = (1..5).asFlow()
.reduce { accumulator, value -> accumulator + value }
println("Sum: $sum")
}
설명:
reduce
는 플로우의 모든 값을 더하여 최종 합계를 반환합니다.
출력:
Sum: 15
3.3 toList
, toSet
- 플로우의 데이터를 리스트(List)나 셋(Set)으로 변환하는 연산자입니다.
fun main() = runBlocking {
val list = (1..5).asFlow()
.toList()
println(list)
}
설명:
toList
는 플로우의 데이터를 리스트로 변환합니다.
출력:
[1, 2, 3, 4, 5]
4. 에러 처리 연산자
4.1 catch
- 플로우에서 발생하는 예외를 처리하는 연산자입니다. 플로우 스트림 내에서 발생한 예외를 잡아 처리합니다.
fun main() = runBlocking {
(1..5).asFlow()
.map { check(it != 3) { "Error on $it" } } // 3에서 오류 발생
.catch { e -> println("Caught $e") }
.collect { value -> println(value) }
}
설명:
catch
는 3에서 발생한 예외를 잡아 처리하고, 그 이후 데이터는 처리하지 않습니다.
출력:
1
2
Caught java.lang.IllegalStateException: Error on 3
4.2 onCompletion
- 플로우가 완료되었을 때(정상 완료 또는 예외 발생 시) 정리 작업을 수행할 수 있는 연산자입니다.
fun main() = runBlocking {
(1..5).asFlow()
.onCompletion { println("Flow completed.") }
.collect { value -> println(value) }
}
설명:
onCompletion
은 플로우가 완료된 후 추가적인 작업을 수행할 수 있도록 합니다.
출력:
1
2
3
4
5
Flow completed.
요약
- 중간 연산자는 플로우의 데이터를 변환, 필터링, 조합하거나 새로운 플로우를 연결하는 데 사용됩니다. 대표적인 연산자로는
map
,filter
,take
,transform
등이 있습니다. - 최종 연산자는 플로우의 데이터를 수집하거나 누적하여 결과를 도출합니다. 대표적인 연산자로는
collect
,reduce
, **
728x90
'개발 언어 > 코틀린' 카테고리의 다른 글
kotlin - coroutine (부록3) Stream VS Flow (6) | 2024.09.05 |
---|---|
kotlin - coroutine (부록2) Channel과 Flow의 사용 예시 (0) | 2024.09.05 |
kotlin - coroutine (부록1) launch VS coroutineScope (0) | 2024.09.05 |
kotlin - coroutine (7) 실제 프로젝트 적용 예제 (2) | 2024.09.05 |
kotlin - coroutine (6) 코루틴 취소 및 타임아웃 (0) | 2024.09.05 |
댓글