Android/Flow

[Android] Flow의 emit은 왜 suspend함수일까?

태크민 2025. 4. 13. 12:56

Kotlin에서 비동기 스트림을 처리할 때 사용하는 Flow는 간결하고 강력한 API를 제공합니다. 특히 Flow를 사용할 때 자주 등장하는 emit() 함수가 눈에 띄는데요, 문서나 코드에서 보면 emit()이 suspend 함수로 정의되어 있다는 것을 알 수 있습니다.

그렇다면, 왜 emit()은 일반 함수가 아니라 suspend 함수로 설계되었을까요? 

결론 부터 말하면, Backpressure(역압) 때문인데요.

그 이유 대해서 자세히 살펴보도록 하겠습니다.


emit()이 하는 일은?

간단히 말해, emit()은 Flow 내부에서 데이터를 외부로 발행(emit) 하는 역할을 합니다.

flow {
    emit(1)
    emit(2)
    emit(3)
}

 

위 코드는 1, 2, 3이라는 값을 순차적으로 소비자에게 보내는 역할을 하죠.

 


왜 suspend여야 할까?

생산자와 소비자의 속도 불균형: Backpressure

Flow는 생산자(emit)소비자(collect) 가 동시에 동작합니다. 하지만 이 둘의 속도가 항상 같지는 않습니다.

예를 들어:

flow {
    for (i in 1..1_000_000) {
        emit(i)
    }
}.collect {
    delay(1000)
    println(it)
}
  • 생산자는 아주 빠르게 100만 개의 데이터를 발행
  • 소비자는 1초에 하나씩만 처리

만약 emit()이 suspend가 아니라 그냥 데이터를 던지는 함수였다면, 소비자가 따라오지 못하는 데이터는 전부 메모리에 쌓이게 됩니다.

 

결과적으로 아래와 같은 문제가 생기죠

  • OutOfMemoryError 발생 가능
  • 앱 성능 저하
  • 기기의 배터리 소모 가속화


해결책: emit()을 suspend로 만들어라!

suspend 키워드를 통해 emit()은 소비자가 데이터를 처리할 때까지 기다릴 수 있는 능력을 가집니다.

즉,

  • 소비자가 느리면 생산자는 멈추고 기다림
  • 메모리에 무분별하게 데이터를 쌓지 않음
  • 생산-소비 속도에 맞는 자연스러운 흐름 형성

생산자-소비자 간의 속도 불균형을 조절하는 것이 바로 Backpressure 처리입니다. 그리고 emit()이 suspend인 이유는 이 backpressure 문제를 안전하게 처리하기 위해서입니다.

 


함께 알아두면 좋은 것들

  • channelFlow와 같은 고급 Flow 빌더에서는 emit()이 내부적으로 채널에 데이터를 전송하기 때문에 더 명확하게 suspend가 필요한 상황이 드러납니다.
  • Flow는 기본적으로 cold stream이며, 데이터를 실제로 소비할 때 실행되므로 suspend 기반이 잘 어울립니다.


마무리

Kotlin의 emit()이 suspend인 이유는 단순한 문법적 선택이 아니라, 안정적이고 예측 가능한 비동기 스트림 처리를 위한 핵심 설계입니다. 특히 생산자와 소비자 간의 처리 속도 차이를 고려한 Backpressure 제어는 고성능 앱 개발에서 매우 중요한 요소입니다.