MutableList 의 데이터를 수정했지만 LiveData 의 Observer가 이를 감지하지 못하는 문제가 발생했다. 로그를 찍어보면 값이 업데이트는 되어있는데, 정작 바뀐 값으로 UI 가 업데이트 되지 않았던 것이다.
Observer 가 변경을 감지하는 시점을 잘못 이해한 것이 원인이었다. LiveData 의 Observer 는 객체의 참조가 변경될 때만 업데이트를 감지한다.
객체 지향 프로그래밍에서 객체의 상태가 변경되지 않는 특성을 '불변 지향'이라고 한다.
Kotlin의 List는 기본적으로 ‘불변(immutable)’이다. 한번 선언하면 그 값을 바꿀 수 없다. 반면 MutableList는 ‘가변’이다. 이 차이 때문에 혼동했다. List 는 선언 후 변경할 수 없고 MutableList 는 값 변경이 가능하니, Observer 가 UI 를 업데이트 하는 시점이 ‘값이 변경되었을 때’라고 생각한 것이다.
하지만 LiveData 의 Observer 는 객체의 참조가 변경될 때만 업데이트를 감지한다. ‘참조가 변경될 때’란 새로운 객체가 만들어져서 새로운 주소값이 할당될 때를 의미한다.
즉, set()이나 add()와 같은 메서드를 사용해서 내부 데이터를 바꾸는 것은 객체의 참조를 변경하지 않기 때문에 observer 는 이를 인식하지 못한다.
LiveData에 새로운 값을 설정하려면 새로운 객체를 만들어야 한다. data class 에서 제공하는 copy() 메서드를 사용하면 일부 속성만 변경하여 새로운 객체를 생성할 수 있다.
수정 전 코드
private var _filterState = MutableLiveData(FilterState.create())
val filterState: LiveData<FilterState> get() = _filterState
val currentState = filterState.value?.hasRatingFilter
if (currentState != null) {
filterState.value?.hasRatingFilter = !currentState // 참조 변경 없이 값만 수정
checkFilterOption(args.conceptId)
}
수정 후 코드
private var _filterState = MutableLiveData(FilterState.create())
val filterState: LiveData<FilterState> get() = _filterState
val currentState = filterState.value?.hasRatingFilter
if (currentState != null) {
// 새로운 객체를 생성하여 LiveData의 값을 업데이트
updateRating(!currentState)
checkFilterOption(args.conceptId)
}
fun updateRating(newState: Boolean) {
val currentState = filterState.value
val updatedState = currentState?.copy(hasRatingFilter = newState) // 불변 객체에서 새로운 객체를 생성
filterState.value = updatedState // 새로운 객체를 LiveData에 할당
}
불변성(Immutable)의 장점을 알아보자
안정성
불변 객체는 한 번 생성되면 수정할 수 없기 때문에, 다른 곳에서 해당 객체를 변경하는 것을 방지할 수 있다.
스레드 안정성(Thread Safety)
멀티스레딩 환경에서 여러 스레드가 같은 변수에 동시에 접근하고 수정할 때, 이 변수의 값이 예기치 않게 변경되거나 잘못된 값으로 설정될 수 있다. 하지만 불변 객체는 데이터가 변경되지 않기 때문에 동기화나 Locking을 사용할 필요가 없고, 여러 스레드에서 안전하게 공유할 수 있다.
단순성
데이터를 읽는 곳과 수정하는 곳을 분리하여 코드가 더 간단하고 예측 가능하게 된다.
평소에 mutableList 에 대해 단순하게 내용을 변경할 수 있다, 없다 수준으로만 알고 있었고 깊게 생각해보지 못했다. 그러나 이번 트러블슈팅을 통해 불변 객체의 필요성에 대해 고민해볼 수 있었고, 코드에 직접 적용해보면서 그 장점을 체감할 수 있었다.
https://developer.android.com/topic/libraries/architecture/livedata?hl=ko