| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- compose
- ktor-auth
- genarics
- rememberupdatedstate
- 2022 kakao blind
- 2989번
- snapshotflow
- producestate
- 양과 늑대
- 무선 페어링
- bottomscaffold
- mutableStateOf
- 안드로이드
- derivedstateof
- apollo3
- remembercoroutinescope
- Java
- 자바
- mutablestate
- android
- 명령형ui
- NavHost
- 명령형 ui
- 선언형 ui
- clean coder
- JCF
- 선언형ui
- State
- 2022 KAKAO BLIND RECRUITMENT
- gradle jdk
- Today
- Total
버미
[안드로이드] MVI에서 Event 처리: Channel vs SharedFlow 본문
MVI(Model-View-Intent)로 앱을 만들다 보면, 사용자 입력(Event)을 처리할 때, Channel를 사용할지 SharedFlow 을 사용할지 고민이 되곤 했다. 이번 포스팅에서 이 둘의 차이점과 MVI패턴의 어떤 상황에서 사용하는 것이 유리할지 정리해보자.
Channel
코루틴 간, 값을 전달하기 위해 설계된 통신 도구이다.
핵심은 보내는 쪽과 받는 쪽의 역할이 명확히 분리되어있다.
이게 무슨 말이냐면,
Channel에서는:
- Sender는 send만 할 수 있다.
- Receiver는 receive만 할 수 있다.
한 쪽이 다른 한 쪽의 상태를 직접 관찰하거나 침범할 수 없다는 것이다.
즉, 생산자와 소비자가 "서로를 모른다"는 것이다.
특징
1. 소비 중심 모델이다
- 이벤트는 받는 쪽이 가져가야만 사라진다.
- 한 번 receive 된 값은 다시 받을 수 없다.
위 특성 때문에, 이벤트는 정확히 한 번만 처리된다.
2. 버퍼 크기(Capacity)
Channel은 버퍼 크기에 따라 동작이 달라진다.
- RENDEZVOUS(0)
- 받는 쪽이 준비되지 않으면 send 가 suspend
- BUFFERED
- 일정량이 버퍼에 쌓임
- UNLIMITED
- 사실상 메모리 주의 필요
Channel은 이벤트가 얼마나 쌓일 수 있는지,
생산자가 소비자를 얼마나 기다릴지를 제어할 수 있다.
이는 이벤트 발생 빈도가 큰 경우에 중요하다.
SharedFlow
SharedFlow의 목표는 Flow를 여러 곳에서 동시에 수집할 수 있도록 하는 것이다.
특징
Hot Flow
- 수집 여부와 상관없이 값을 emit한다.
- 값은 흘러가고 Collector는 그 흐름에 참여 한다.
Broadcast Stream
- 하나의 emit에 대해서 여러 Collector에게 동시 전달이 가능하다.
- Collector 간에는 서로 독립이다.
┌─ Collector A
Emitter ──┼─ Collector B
└─ Collector C
이벤트 정책
SharedFlow는 기본적으로 이벤트를 보장하지 않는다.
대신, 정책을 개발자가 선택할 수 있다.
- replay
- 새 Collector가 과거 이벤트를 받을지
- extraBufferCapacity
- 순간 폭주 흡수
- onBufferOverflow
- 넘치면 멈출지 / 버릴지
SharedFlow는 '스트림'을 전제로 하기 때문에,
기본적으로 큐(버퍼)를 중심 개념으로 갖지 않는다.
정책 상, 선택적으로 버퍼를 사용한다.
Channel은 처음부터 큐를 중심으로 설계된 구조여서
버퍼(capacity)가 통신 의미와 강하게 결합되어 있다.
Channel은 다음 조건에 잘 맞는다.
- 이벤트는 반드시 한 번만 처리되어야 할 때
- 저장, 제출, 결제 등
- 입력 순서가 의미 있을 때
- 단일 이벤트 처리 루프를 유지하고 싶을 때
- Event → Reduce → State/Effect 흐름이 명확
- 이벤트 유실이나 중복 처리를 피하고 싶을 때
SharedFlow는 다음 조건에 잘 맞는다.
- 이벤트가 연속적/빈번하게 발생할 때
- 검색어 입력, 스크롤, 드래그
- 최신 입력만 처리해도 될 때
- 이전 요청 취소하고 최신만 반영
- Flow 연산자로 이벤트를 가공하고 싶을 때
- debounce, throttle, flatMapLatest 등
- 여러 곳에서 이벤트를 받거나 관찰할 가능성이 있을 때
필자는 Channel과 SharedFlow를 고민하다가 SharedFlow를 선택하게 되었다.
그 이유는 아래와 같다.
1. 안드로이드 생명주기와의 궁합
- repeatOnLifecycle 같은 패턴과 자연스럽게 결합이 가능하다.
- UI는 스트림을 구독하고 해제하는 역할만 담당하고,
이 구독/해제의 타이밍이 화면의 활성/비활성 상태와 맞물리면서
마치 생명주기 이벤트에 반응하는 것처럼 보인다.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.events.collect { event ->
handle(event)
}
}
}
2. debounce/ flatMapLatest 같은 체인 제공
입력 이벤트를 스트림으로 다루는 경우,
debounce, flatMapLatest 같은 Flow 연산자를 바로 적용할 수 있다는 점이 크다.
Channel에서도 consumeAsFlow()로 변환하면 유사한 처리는 가능하지만,
이 경우 스트림 가공을 위해 구조가 한 단계 더 복잡해진다.
입력 이벤트를 사용하기 편하게 가공한다는 관점에서는
SharedFlow가 더 자연스럽게 느껴졌다.
위의 이유 때문에 결국 안드로이드에서 UI-Compose의 궁합에 잘 맞게 된 것 같고 선택하게 된 계기가 되었다.
'안드로이드' 카테고리의 다른 글
| [안드로이드] 웹뷰와 커스텀 탭 (0) | 2026.03.23 |
|---|---|
| [안드로이드] JDK, JRE, JVM 그리고 Gradle JDK (0) | 2025.10.09 |
| [안드로이드] 안드로이드 스튜디오 무선 연결이 안될 때 (0) | 2025.10.02 |
| [안드로이드] Gson과 kotlinx.serialization 차이 (0) | 2025.09.23 |
| [안드로이드] Gradle 이란 (0) | 2025.09.16 |