StateFlow vs SharedFlow 를 비교해보자 #이벤트 핸들링
오늘은 StateFlow 와 SharedFlow 에 대해 비교해 보도록 하겠습니다.
1. 기존 글 참조
만약 SharedFlow와 StateFlow의 기본에 대해 정리하고 싶으실경우,
아래 글들을 참조하신 다음에 이 글을 읽으면 더욱 도움이 될 것 입니다.
>> SharedFlow 에 대한 총정리 # Buffer Replay tryEmit Kotlin Coroutine
>> StateFlow 정리 # Android Kotlin Coroutine getStateFlow StateIn
2. StateFlow 는 State 변화를 위한 API
2-1. StateFlow 는 이벤트핸들링을 위한 API가 아니다
아래는 Kotlin 이 아닌 Android의 공식문서인데요.
중요한 부분이 잘 설명되어 있으니 보도록 하겠습니다.
StateFlow는 flow의 state-holder라고 되어 있고요.
state을 업데이트하기위해서는,
새로운 값은 value property에 넣어주어야 한다고 되어 있습니다.
만약 아래와 같은 시나리오를 생각해 보겠습니다.
- 유저가 버튼을 클릭
- 기존에 정의한 네트워크 flow를 실행해 데이터 받아옴
억지스럽지만, 아래와 같은 코드가 있다고 가정해 보겠습니다.
StateFlow는 초기값이 반드시 필요하니,
아래에서 사용한 mutableStateFlow에서도 반드시 초기값이 필요합니다.
(때로는 의미없는 값을 넣어줘야 할 때도 있습니다)
private val _refreshTrigger = MutableStateFlow(true)
private val refreshTrigger = _refreshTrigger.asStateFlow()
refreshTrigger에서 flow 데이터가 흘러나오면 네트워크를 호출하도록 되어 있습니다.
var networkResult = refreshTrigger
.flatMapLatest {
repository.fetchNetworkResults()
}
유저가 버튼을 클릭하면 viewModel에서 아래와 같은 코드가 호출되도록 하였다고 가정해 보겠습니다.
viewModelScope.launch {
_refreshTrigger.emit(true)
}
이 경우, 새로운 네트워크 호출이 이루어질까요?
정답은 아닙니다 입니다.
왜냐하면, 위의 안드로이드 공식문서의 설명처럼,
value 값의 변화가 없는 상태이므로,
값을 업데이트하기위해,
flow 데이터를 흘려보내주지 않기 때문입니다.
그렇다고, 클릭할 때마다,
의미없이 한번은 true, 한번은 false를 토글해서 보내면,
매번 기존값을 확인해야 하는 번거로움이 발생하게 됩니다.
애초에, StateFlow는,
이런 이벤트를 핸들링하거나 이벤트에 트리거되도록 설계된 API가 아니기 때문입니다.
2-2. 항상 최신 값을 받게 되는 API
StateFlow는 가장 최신의 업데이트(변경이 있는) 된 값을 흘려보내주는 API입니다.
만약 아래와 같이 A, B, C 값이 차례로 흘러나오고 있을 때,
언제 Collecting 하는 냐에 따라서 받는 값이 달라집니다.
2번 시점에 Collect하기 시작하면 받는 값은 B 가 됩니다.
Collect 하기 이전에 방출된 값이라도 최신의 값을 전달해 주는 것 입니다.
언제 Collect 하던지 가장 최신의 값을 전달받을 수 있는데요.
이러한 개념은 StateFlow의 존재이유이기도 한데요.
________ ________ ________
| Value A | ------> | Value B | ------> | Value C | ------>
-------- -------- --------
(1) (2) (3)
2-3. 한번에 하나의 값만을 가짐
StateFlow는 일반적인 flow와는 다르게,
여러개의 값을 가질 수 없습니다.
가장 최신의 하나의 값만을 가질 수 있는 API 입니다.
그렇다고 list나 Pair 같은 데이터타입을 못 쓰는 것이라는 의미는 아닙니다.
3. SharedFlow
SharedFlow는 여러 콜렉터들에게 flow데이터를 전달해 줄 수 있는 Flow API 인데요.
안드로이드 문서에서 예시로 든 것이 있는데요.
앱 전체에 tick을 보내서,
정기적으로 앱의 데이터들이 refresh되게 할 수 있다고 나와있습니다.
이름만 들으면 여러곳에서 collect할 때는 SharedFlow,
아닐때는 StateFlow로 생각할 수도 있지만,
그것만을 기준으로 둘을 나누어 사용할 수는 없습니다.
StateFlow에 한개의 collector만 붙을 수 있는 것이 아니기 때문입니다.
3-1. 이벤트 핸들링에 더 적합
SharedFlow는 StateFlow보다도 이벤트핸들링에 오히려 더욱 적합합니다.
위의 안드로이드 공식문서에도 나온 것 처럼,
프로그램 전체에 tick을 보내서 값을 리프레쉬 할수도 있구요.
StateFlow처럼, 값이 변하는 경우에만 flow데이터를 보내는 API가 아니기 때문입니다.
private val _refreshTrigger = MutableSharedFlow<Unit>()
private val refreshTrigger = _refreshTrigger.asSharedFlow()
초기화 값을 반드시 넣어주어야 했던, MutableStateFlow 와는 다르게,
이 API에서는 초기화 값을 넣어줄 필요도 없습니다.
마찬가지로 아래와 같이 emit을 할 때도 Unit을 넣어주어도 문제없이 동작합니다.
viewModelScope.launch {
_refreshTrigger.emit(Unit)
}
위에서 보았던, 제대로 동작하지 않는 refreshTrigger는 SharedFlow를 이용하면,
문제없이 동작하게 됩니다.
특히나 일회성 이벤트 처리에 효율적인데요.
에러가 발생했을 때 토스트 메시지를 보여주어야 한다고 가정해 보겠습니다.
이런 동작은 한번만 일어나야 하는데요.
StateFlow로 처리해 버리면,
화면이 회전되거나, configuration change가 발생하면,
이전 상태가 다시 전달되,
동일한 토스트가 다시 표시되버릴 수 있습니다.
3-2. Collecting 이 되면 값을 흘려보내주는 API
위에서 StateFlow는 collecting 하는 시점의 최신 값을 흘려보내준다고 하였는데요.
SharedFlow는 조금 다릅니다.
Collecting을 시작하는 시점이후에 방출되는 값들을 받게 됩니다.
아래에서 1번시점에 Collecting을 하게 된다면,
A가 최신값임에도 받지 못하고, B부터 받게 됩니다.
(반면, StateFlow에서는 1번시점에 Collecting하면 A값을 바로 최신값으로 받을 수 있었지요.)
________ ________ ________
| Value A | ------> | Value B | ------> | Value C | ------>
-------- -------- --------
(1) (2) (3)
3-3. 여러개의 값을 한번에 보낼 수 있음
StateFlow가 하나의 single 값만을 보낼 수 있었는데요.
SharedFlow는 여러개의 값을 한번에 보낼 수 있습니다.
일반적인 Flow와 이 부분에서는 동일합니다.
3-4. Buffer 나 Replay 같은 옵션을 제공
이 부분은 아래 글에 정리되어 있는데요.
StateFlow와는 다르게, 좀 더 많은 옵션들을 제공해 줍니다.
>> SharedFlow 에 대한 총정리 # Buffer Replay tryEmit Kotlin Coroutine
4. 비교
마지막으로 간단하게 도표로 둘을 비교해 보도록 하겠습니다.
구분 | StateFlow | SharedFlow |
초기화 값 | 반드시 필요 | 필요없음 (의미없는 값 넣을 필요없음) |
이벤트 핸들링 | 적합하지 않을 수 있음. 변경되는 최신값만 흘려보내 줌 |
적합함 일회성 이벤트에 좋음 (안드로이드에서 화면 회전등 configuration change가 일어나더라도, 지나간 이벤트를 다시 전달하지 않음) |
하나의 값만 가짐 | 하나의 값만 보낼 수 있으며, 최신값으로 항상 유지 |
여러개의 값을 방출할 수 있음 |
Buffer, Replay 옵션 | 없음 | 있음. |
방출(emit) 시점 | collecting 시작되면, 가장 최신값을 방출해 줌 |
Collecting이 시작되고 나면, 기존의 최신값은 방출해주지 않고, 이 후 방출되는 값부터 방출해 줌. 다만, buffer나 replay로 이런 것들을 컨트롤 할 수 있음 |
멀티 컬렉터 지원 | 여러 collector 들에서 Collecting 가능 | 동일(여러 Collector 들이 Collect 가능) |
주 용도 | State 변경이나 최신값만 중요한 dataStream 에 적합 |
이벤트나 DataStream 에 적합 |
이상으로 StateFlow vs SharedFlow 에 대해서 비교해 보았습니다.