LiveData를 단일 이벤트 처리에 사용하는 방법은?
_____A1: LiveData는 구독 상태에 따라 최신 데이터를 계속 전달하는 특성이 있어, 화면 회전 등으로 재구독될 때 이벤트가 다시 발생하는 문제가 있습니다. 즉, 단일 이벤트(예: Toast 메시지, 네비게이션 명령 등)는 중복 발생하지 않아야 하는데 LiveData는 이를 보장하지 않습니다.
Q2: 단일 이벤트란 정확히 무엇을 의미하나요?
A2: 단일 이벤트는 한 번만 처리되어야 하는 데이터나 동작을 의미합니다. 예를 들어, “로그인 성공 알림”, “일회성 네비게이션 명령” 등이 해당하며, 같은 이벤트를 여러 번 처리할 경우 부작용이 발생할 수 있습니다.
Q3: LiveData를 단일 이벤트 처리에 적합하도록 사용하는 대표적인 방법은 무엇인가요?
A3: 크게 3가지 방법이 널리 사용됩니다.
1. Event Wrapper 클래스 사용
2. SingleLiveEvent 커스텀 클래스 활용
3. Channel + Flow 조합 사용 (코루틴 환경에서)
Q4: Event Wrapper 클래스란 무엇인가요?
A4: 이벤트 값을 감싸서, 이벤트가 한 번만 소비되도록 하는 래퍼 클래스입니다. 일반적으로 내부에 ‘consumed’ 플래그가 있어 한 번 읽힌 후에는 재사용 불가하도록 구현합니다.
```kotlin
open class Event
private var hasBeenHandled = false
fun getContentIfNotHandled(): T? = if (hasBeenHandled) null else {
hasBeenHandled = true
content
}
fun peekContent(): T = content
}
```
ViewModel에서 이벤트를 `LiveData
Q5: SingleLiveEvent란 무엇이고 어떻게 사용하나요?
A5: SingleLiveEvent는 구글과 커뮤니티에서 주로 쓰이는 커스텀 LiveData 서브클래스로, 관찰자에 이벤트를 한 번만 전달합니다. 재구독 시 동일 이벤트가 재전달되지 않도록 구현되어 있습니다. 오픈소스 구현체가 많으며, 아래와 같이 사용합니다.
class SingleLiveEvent
private val pending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer
super.observe(owner) { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
}
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
}
```
ViewModel에 `val event = SingleLiveEvent
Q6: 코루틴 환경에서는 어떻게 단일 이벤트를 처리하나요?
A6: Channel이나 SharedFlow를 활용해 단일 이벤트를 처리합니다. 예를 들어, `Channel
Q7: 정리하면, LiveData에서 단일 이벤트를 안전하게 처리하려면 어떻게 해야 하나요?
A7:
- Event Wrapper 클래스로 소비 여부 제어
- SingleLiveEvent 같은 커스텀 LiveData 클래스 활용
- 최신 코루틴 환경에서는 Channel, SharedFlow 등으로 대체
이 중 상황과 프로젝트 환경에 맞게 선택해 사용하면 됩니다.
---
즉, LiveData 자체는 상태 데이터 전달에 최적화되어 있으므로, 단일 이벤트는 Event Wrapper 또는 커스텀 SingleLiveEvent, 코루틴 기반 솔루션을 활용해 처리하는 것이 권장됩니다.
하지만 LiveData는 기본적으로 상태 기반(State-based)이며, 이벤트 기반(Event-based) 동작에는 몇 가지 한계가 있습니다.
예를 들어, 화면 회전 등으로 액티비티/프래그먼트가 재생성될 때 LiveData의 값이 재전달되어, 단일 이벤트(예: 토스트 메시지 표시, 네비게이션)도 다시 발생하는 문제가 있습니다.
즉, LiveData는 상태 변화를 저장하고 관찰자에게 지속해서 최신 상태를 전달하지만, '한 번만 처리되어야 하는 이벤트'를 다루기에는 적합하지 않습니다.
이런 단일 이벤트(single event)를 올바르게 처리하기 위해 몇 가지 대표적인 패턴이 존재합니다.
1. Event Wrapper 사용 가장 흔히 사용되는 방법은 이벤트 내용을 래핑하는 클래스(Event wrapper)를 만들어 LiveData가 이 객체를 관찰하도록 하는 것입니다.
이 클래스는 이미 이벤트가 처리되었는지 여부를 추적해, 같은 이벤트가 중복 처리되지 않도록 만듭니다.
예시로 다음과 같은 클래스를 만들 수 있습니다.
```kotlin open class Event
```kotlin private val _singleEvent = MutableLiveData
```kotlin viewModel.singleEvent.observe(this) { event -> event.getContentIfNotHandled()?.let { message -> Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } } ``` 이 방식의 장점은: - 이벤트가 한 번만 처리되도록 보장 - 같은 이벤트가 두 번 이상 중복 실행되는 것을 방지 - LiveData 기본 메커니즘을 그대로 사용 가능 단점은: - 이벤트 래퍼 클래스 코드를 별도로 작성해야 함 - 약간의 boilerplate 코드 존재 ---
2. SingleLiveEvent 사용 Google이나 커뮤니티에서 만들어진 `SingleLiveEvent` 라이브러리를 사용하는 방법도 있습니다.
`SingleLiveEvent`는 내부적으로 한 번만 이벤트를 전달하도록 구현된 LiveData의 서브클래스입니다.
`SingleLiveEvent`는 다음과 같은 특징이 있습니다.
- 오직 한 번만 값이 전달됨 - 여러 옵저버가 있을 때 하나의 옵저버에만 이벤트가 전달되는 문제 존재 (대부분 단일 소비자가 있어 문제가 없다) - 별도의 이벤트 래핑없이 사용 가능 사용 예시는 다음과 같습니다.
```kotlin private val _singleEvent = SingleLiveEvent
단점: - SingleLiveEvent의 구현 결정권이 라이브러리나 직접 구현한 코드에 따라 다름 - 여러 옵저버가 있을 때 예상치 못한 동작이 발생할 수 있음 ---
3. 코루틴과 Channel, Flow 사용 (최근 권장 방법) 요즘 안드로이드에서는 코틀린의 코루틴을 활용하는 방식을 권장합니다.
특히 `StateFlow`나 `SharedFlow` 등 Flow API를 활용하면 단일 이벤트 처리에 매우 적합합니다.
예를 들어 `SharedFlow`를 `MutableSharedFlow
- MutableSharedFlow는 버퍼 크기와 replay 옵션을 활용해서 이벤트를 한 번만 제공하도록 설정 가능 - LiveData와 달리 재구독 시에 이벤트가 중복 전달되는 문제 없음 간단 예시: ViewModel ```kotlin private val _eventFlow = MutableSharedFlow
작성자:
김하윤 [비회원]
| 작성일자: 1년 전
2025-05-25 12:41:14
조회수: 151 | 댓글: 0 | 좋아요: 0 | 싫어요: 0
조회수: 151 | 댓글: 0 | 좋아요: 0 | 싫어요: 0
내용이 부정확하다면 싫어요를 클릭해주세요.