Android/Flow

[Android] 이벤트 처리 Channel vs SharedFlow

태크민 2025. 2. 18. 18:45

안드로이드에서 이벤트(사이드 이펙트)는 주로 channel 또는 sharedFlow를 사용해 처리한다.

channel을 이용한 이벤트 처리 예시 코드

private val _effect: Channel<A> = Channel()
val effect = _effect.receiveAsFlow()

sharedFlow를 이용한 이벤트 처리 예시 코드

private val _sideEffectFlow: MutableSharedFlow<SE>
val sideEffectFlow: SharedFlow<SE> = _sideEffectFlow.asSharedFlow()

 

그렇다면 SharedFlow와 Channel을 사용한 이벤트 처리의 차이점은 뭘까?

각각의 장단점에 대해 알아보자.

 


Channel

장단점

장점 : 백그라운드에서 발생한 이벤트도 수집 가능

단점 : 여러 개의 구독자를 가지기에는 적합하지 않음

 

테스트 1 - 백그라운드

MainViewModel

class MainViewModel : ViewModel() {
    private val _channel = Channel<Int>()
    val channel = _channel.receiveAsFlow()

    init {
        viewModelScope.launch {
            repeat(100) {
                Log.d("Channel", "MainViewModel - Send $it")
                _channel.send(it)
                delay(1000)
            }
        }
    }
}

MainActivity

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.channel.collect { number ->
                    Log.d("Channel","MainActivity - Collected $number from channel")
                }
            }
        }
        // ...
}
 

결과

  1. MainActivity - onStop (백그라운드 진입)
  2. MainViewModel - channel send 7 (백그라운드에서 send)
  3. MainActivity - onStart (포그라운드 진입)
  4. MainActivity - collect 7 (포그라운드에서 collect)

백그라운드에서 send한 7을 올바르게 collect한 모습을 볼 수 있다.

 

이게 가능한 이유는 channel의 send()는 channel의 버퍼가 꽉 차있거나 존재하지 않으면 suspend되기 때문이다. 

(suspending the caller while the buffer of this channel is full or if it does not exist, or throws an exception if the channel is closed for send (see close for details).)

 

테스트 2 - 여러 개의 구독자를 가지는 경우

MainActivity

lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.channel.collect { number ->
                    Log.d("Subscriber","Subscriber[1] - Collected $number from channel")
                }
            }
        }

lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            viewModel.channel.collect { number ->
                Log.d("Subscriber","Subscriber[2] - Collected $number from channel")
            }
        }
    }

 

결과

 

Channel에서 여러 개의 구독자가 있다면, 각각의 구독자가 번갈아가면서 collect하게 된다.

  1. channel - send 1
  2. subscriber[1] - collect 1
  3. channel - send 2
  4. subscriber[2] - collect 2

공식 문서에 따르면 Channel은 평등하기 때문이다.

따라서 여러 개의 구독자를 가지는 경우, 각각의 구독자가 같은 이벤트를 수신하지 않으므로 Channel보다는 SharedFlow가 더 적합하다.

 

예를 들어, 앱 전체에 tick을 보내서 정기적으로 앱의 데이터를 refresh 해야한다면 Channel을 사용하는 것은 부적합하다.

 


 

sharedFlow

장점 : 여러 개의 구독자를 가질 수 있음

단점 : 백그라운드에서 발생한 이벤트 수집 불가능

 

테스트 1 - 백그라운드

MainActivity

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.sharedFlow.collect { number ->
            Log.d("SharedFlow","MainActivity - Collected $number from sharedFlow")
        }
    }
}

결과

  1. MainActivity - onStop (백그라운드 진입)
  2. MainViewModel - sharedFlow emit 8, 9, 10, 11 (백그라운드에서 emit)
  3. MainActivity - onStart (포그라운드 진입)
  4. MainActivity - collect 12 (포그라운드에서 collect) - 8, 9, 10, 11은 유실됨

백그라운드에서 emit한 8, 9, 10, 11은 유실된 것을 확인할 수 있다.

 

테스트 2 - 여러 개의 구독자를 가지는 경우

MainActivity

lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.sharedFlow.collect { number ->
                    Log.d("Subscriber","Subscriber[1] - Collected $number from sharedFlow")
                }
            }
        }

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.sharedFlow.collect { number ->
                    Log.d("Subscriber","Subscriber[2] - Collected $number from sharedFlow")
                }
            }
        }

 

결과

 

여러 개의 구독자를 가져도 모든 구독자가 같은 이벤트를 collect하는 것을 확인할 수 있다.

 


결론

이벤트(side-effect)는 보통 한 곳에서만 처리를 한다.

 

따라서 이벤트는 보통 한 곳에서만 처리를 하므로 channel을 사용하여 이벤트를 수신하는게 가장 코드를 덜 작성하고 비교적 쉽게 이해할 수 있다.

필요한 경우에만 sharedFlow를 사용하는 것이 좋다 생각한다.

 


출처

https://jinukeu.hashnode.dev/android-channel-vs-sharedflow

 

[Android] 이벤트 처리 Channel vs SharedFlow

안드로이드에서 이벤트(사이드 이펙트)는 주로 channel 또는 sharedFlow를 사용해 처리한다. channel을 이용한 이벤트 처리 예시 코드 private val _effect: Channel<A> = Channel() val effect = _effect.receiveAsFlow() sharedF

jinukeu.hashnode.dev

https://medium.com/prnd/mvvm%EC%9D%98-viewmodel%EC%97%90%EC%84%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A5%BC-%EC%B2%98%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-6%EA%B0%80%EC%A7%80-31bb183a88ce

 

MVVM의 ViewModel에서 이벤트를 처리하는 방법 7가지

ViewModel의 이벤트 처리를 어떻게 하고 계신가요? 헤이딜러에서 LiveData -> SingleLiveData -> SharedFlow -> EventFlow -> Channal로 이벤트 처리 방법을 변화 하기까지 과정을 소개합니다

medium.com