11월 4주차에는 안드로이드 메모리 개선 - 최신 Android Runtime 업데이트에 대한 글입니다.
https://proandroiddev.com/viewmodel-events-as-state-are-an-antipattern-35ff4fbc6fb6https://funkymuse.dev/posts/nav-type-safe/#until-next-time
ViewModel: Events as State are an Antipattern
In this article Nikita explores how a popular notion about expressing events as states may be a misleading one.
proandroiddev.com
소개
이 글에서는 Kotlin 개발자 커뮤니티에서 논쟁이 되고 있는 "원-오프(one-off) 이벤트"에 대한 안티패턴을 다룹니다. Manuel Vivo의 글 "ViewModel: One-off event antipatterns"에 대한 반론을 제시하며, 이벤트를 상태 변수로 표현하는 것이 왜 문제가 될 수 있는지 설명합니다.
내용
- Vivo의 주장: UI/UI 로직 계층에서 발생하는 일회성 이벤트는 객체 스트림 대신 상태 변수로 표현되어야 함.
- 반론: ViewModel에서 이벤트를 노출하는 것은 ViewModel이 상태의 진실된 출처가 아니라는 것을 의미하지 않음. 또한, UDF(Unidirectional Data Flow)는 생산자보다 오래 지속되는 소비자에게 이벤트를 전달하는 이점을 언급하지 않음.
이벤트 처리 방식
- SharedFlow와 Channel을 사용하여 이벤트를 노출하는 방식은 이벤트의 전달 및 처리를 보장하지 않습니다. 이는 일회성 이벤트에 적합하지 않습니다.
- 예시: SharedFlow를 사용하여 이벤트를 노출하면 구독자가 없을 때 이벤트가 무시됩니다. 이는 여러 구독자가 동일한 이벤트를 처리하는 경우 문제가 발생할 수 있습니다.
상태로서의 이벤트 표현의 문제점
- 이벤트를 상태로 표현하는 것은 개발자들에게 이해하기 어렵고, 일관성 있는 코드 유지 관리가 어려워집니다. 이는 실제 세계에서 일어나는 사건의 본질과 모순됩니다.
예시 코드
1. 이벤트를 상태 변수로 표현하는 방식
// ViewModel 예시
class PaymentViewModel : ViewModel() {
private val _paymentState = MutableLiveData<PaymentState>()
val paymentState: LiveData<PaymentState> = _paymentState
fun completePayment() {
// 결제 로직 수행
_paymentState.value = PaymentState.Success
}
}
// UI 컴포넌트에서 상태 처리 예시
@Composable
fun PaymentScreen(viewModel: PaymentViewModel) {
val paymentState by viewModel.paymentState.observeAsState()
when (paymentState) {
is PaymentState.Success -> {
// 결제 성공 UI 처리
}
// 기타 상태 처리
}
}
2. 이벤트를 별도의 이벤트 스트림으로 처리하는 방식
// ViewModel 예시
class PaymentViewModel : ViewModel() {
private val _paymentEvent = Channel<PaymentEvent>()
val paymentEvent = _paymentEvent.receiveAsFlow()
fun completePayment() {
// 결제 로직 수행
_paymentEvent.offer(PaymentEvent.PaymentCompleted)
}
}
// UI 컴포넌트에서 이벤트 처리 예시
@Composable
fun PaymentScreen(viewModel: PaymentViewModel) {
val paymentEvent by viewModel.paymentEvent.collectAsState(initial = PaymentEvent.Idle)
LaunchedEffect(paymentEvent) {
when (paymentEvent) {
is PaymentEvent.PaymentCompleted -> {
// 결제 완료 이벤트 처리
}
// 기타 이벤트 처리
}
}
}
위 두 예시 코드에서 첫 번째 방식은 이벤트를 상태 변수로 관리하여 UI가 상태에 반응하도록 구현한 것이고, 두 번째 방식은 이벤트를 별도의 스트림으로 처리하여 UI가 이벤트 발생에 반응하도록 구현한 것입니다. 첫 번째 방식은 상태 변화에 초점을 맞춘 반면, 두 번째 방식은 이벤트의 발생 자체에 초점을 맞춥니다.
정리
이 글의 저자는 이벤트를 상태로 처리하는 접근 방식이 자연스럽지 않고, 실제 애플리케이션 개발에서 여러 문제를 야기한다고 주장합니다. 또한, 이러한 접근 방식이 코드의 가독성과 유지 관리 가능성을 저하시키고, 더 큰 버그와 사용자 만족도 저하로 이어질 수 있음을 지적합니다. 따라서 이벤트와 상태를 구분하여 처리하는 것이 더 나은 접근 방식이라고 결론짓는 글 이였습니다.
Android Weekly - Free weekly Android & Kotlin development newsletter
Android Weekly - Free weekly Android & Kotlin development newsletter
androidweekly.net
'Android Weekly' 카테고리의 다른 글
Android Weekly #-600 "Baseline Profiles를 이용한 앱 성능 향상" (0) | 2023.12.13 |
---|---|
Android Weekly #-599 "구글 개발자들이 좋아하는 도구와 조언" (0) | 2023.12.13 |
Android Weekly #-597 "안드로이드 메모리 개선 - Runtime 업데이트" (0) | 2023.12.12 |
Android Weekly #-596 "Android 앱 배포 전 6단계 확인과정" (0) | 2023.12.12 |
Android Weekly #-595 "리스트 선택 창의 애니메이션" (0) | 2023.12.12 |