개발 언어/코틀린

kotlin - coroutine (부록3) Stream VS Flow

jjiiiinn 2024. 9. 5. 15:06
728x90

1. Stream과 Flow의 차이점

1.1 동기 vs 비동기

  • Stream: 자바의 Stream API동기적으로 작동합니다. 이는 한 번에 하나의 스레드에서만 데이터를 처리할 수 있으며, 비동기 처리에는 적합하지 않습니다.
  • Flow: 비동기적으로 데이터를 처리할 수 있도록 설계된 코틀린의 API입니다. 플로우는 코루틴을 기반으로 하며, 동시에 여러 작업을 처리할 수 있습니다. 지연된 데이터를 처리하거나 리액티브 프로그래밍이 필요할 때 유리합니다.

1.2 백프레셔(Backpressure) 지원

  • Stream: 자바의 스트림은 백프레셔를 지원하지 않습니다. 백프레셔는 데이터가 너무 빨리 방출되어 소비자가 이를 감당할 수 없는 상황을 처리하는 메커니즘인데, 스트림은 데이터 소비 속도를 제어할 방법이 없습니다.
  • Flow: 백프레셔를 자연스럽게 처리할 수 있습니다. 이는 플로우가 리액티브 프로그래밍에 맞게 설계되어 있으며, 소비자가 데이터를 처리할 수 있을 때만 데이터를 전달합니다.

1.3 콜드 스트림 vs 핫 스트림

  • Stream: 자바의 스트림콜드 스트림입니다. 이는 데이터를 요청할 때만 계산을 시작하는 방식을 의미하며, 요청이 있을 때마다 항상 새로 계산됩니다.
  • Flow: Flow도 기본적으로 콜드 스트림입니다. 데이터를 요청하는 시점에 데이터 흐름이 시작되며, 이전에 방출된 데이터는 다시 수신되지 않습니다.

1.4 차가운(Cold) vs 뜨거운(Hot) 스트림의 지원

  • Stream: 자바의 스트림은 항상 차가운 스트림(Cold Stream)만을 지원합니다. 이는 데이터를 요청할 때마다 새로 데이터를 제공한다는 의미입니다.
  • Flow: 플로우는 기본적으로 차가운 스트림을 제공하지만, SharedFlowStateFlow를 통해 뜨거운 스트림(Hot Stream)도 지원합니다. 뜨거운 스트림은 이미 방출된 데이터를 여러 소비자가 수신할 수 있는 방식입니다. 이를 통해 실시간 이벤트 스트림과 같은 동작을 지원합니다.

1.5 멀티스레드 지원

  • Stream: 자바의 Stream멀티스레드 환경에서 기본적으로 사용할 수 없으며, 병렬 스트림(parallelStream)을 사용해야만 멀티스레드 처리가 가능합니다.
  • Flow: 플로우멀티스레드 환경에서 자연스럽게 작동하며, 별도의 설정 없이도 비동기 작업을 병렬로 처리할 수 있습니다. 코루틴을 활용하여 여러 스레드에서 효율적으로 동작합니다.

2. Stream과 Flow 사용 시점

언제 Stream을 사용해야 할까?

  1. 동기적 데이터 처리가 필요한 경우:
    • 단일 스레드에서 순차적으로 데이터를 처리할 때는 Stream이 적합합니다. 자바의 스트림은 동기적 작업 처리에 뛰어나며, 큰 데이터 집합을 필터링, 변환, 집계할 때 유용합니다.
    • 예를 들어, 컬렉션에서 데이터를 필터링하고 동기적으로 처리할 때 Stream을 사용하는 것이 좋습니다.
  2. 단순한 동기 작업:
    • 비동기 처리가 필요하지 않은, 단일 스레드에서 순차적 데이터 처리만 필요한 상황에서 Stream을 사용하는 것이 적합합니다.

언제 Flow를 사용해야 할까?

  1. 비동기 데이터 처리가 필요한 경우:
    • 네트워크 요청, 파일 입출력, 센서 데이터 수집 등 비동기 작업을 처리할 때 Flow를 사용하면 좋습니다. 코루틴을 기반으로 하여 비동기 작업을 효율적으로 처리하고, 비동기 스트림을 자연스럽게 구현할 수 있습니다.
  2. 리액티브 프로그래밍:
    • 플로우는 리액티브 프로그래밍을 지원하므로, 백프레셔가 필요하거나, 실시간 데이터 스트림(주식 가격, 센서 데이터 등)을 처리하는 경우에 유리합니다.
  3. 멀티스레드 환경:
    • 멀티스레드 환경에서 여러 비동기 작업을 동시에 처리해야 하는 경우, Flow가 자연스럽게 작동하며 코루틴을 기반으로 안전하게 동작합니다.
  4. 백프레셔가 필요한 경우:
    • 데이터 생산 속도가 빠르고, 소비자가 그 데이터를 따라잡기 어려운 상황에서 백프레셔 처리를 위해 Flow를 사용하는 것이 적합합니다.

예시: Stream 사용 (동기적 데이터 처리)

import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        Stream.of(1, 2, 3, 4, 5)
            .filter(num -> num % 2 == 0)
            .map(num -> num * 2)
            .forEach(System.out::println);
    }
}

예시: Flow 사용 (비동기 데이터 처리)

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking {
    (1..5).asFlow()
        .filter { it % 2 == 0 }
        .map { it * 2 }
        .collect { println(it) }
}

3. Flow를 사용해야 하는 이유

  • 비동기 작업에 최적화: 네트워크 요청, 파일 입출력 등 비동기 작업을 효율적으로 처리할 수 있습니다.
  • 리액티브 프로그래밍: Flow리액티브 프로그래밍 패러다임을 지원하며, 데이터 생산자와 소비자 간의 속도 차이를 처리할 수 있는 백프레셔 기능을 제공합니다.
  • 멀티스레드 작업: 비동기적 멀티스레드 환경에서 안전하게 작업을 병렬 처리할 수 있습니다.
  • 플로우의 연산자: map, filter, transform 등의 연산자를 활용하여 데이터 스트림을 유연하게 변환하고, 백프레셔에러 처리를 지원합니다.

요약

  • Stream동기적 데이터 처리에 적합하며, 주로 순차적 작업이 필요한 상황에서 사용됩니다.
  • Flow비동기적 데이터 처리리액티브 프로그래밍을 지원하며, 특히 네트워크 요청이나 백프레셔가 필요한 작업에 적합합니다.
  • 멀티스레드 환경에서 비동기 작업을 효율적으로 처리하려면 Flow를 사용하는 것이 더 유리합니다.

간단하게, 일반적인 동기적 프로그래밍 작업에서는 **Stream을 사용하고, 비동기적 작업을 처리할 때는 코루틴 기반의 Flow를 사용한다고 생각하면 됩니다.**

728x90