private val _setter = MutableLiveData(R.id.home_fragment)
val setter: LiveData<Int> = _setter
private val _getter = MutableLiveData(R.id.home_fragment)
val getter: LiveData<Int> get() = _getter
다양한 예제들을 볼 때마다 위와 같은 코드 형식을 볼 수 있다. 구글 공식 예제인 sunflower 에서도 위와 같은 방식을 사용한다. 이거.. 왜 이렇게 하는 것일까? 그리고 =
방식을 사용하기도 하고 get()
방식을 사용하기도 하던데 어떤 차이점이 있을까?
참고로 private 변수의 밑줄은 코틀린 컨벤션이다.
위와 같은 코드 형식을 Backing Property 라고 표현한다. Mutable 타입의 프로퍼티를 private으로 선언하고 Immutable 타입의 프로퍼티를 public으로 선언하여 외부에서 값을 변경하기 위해 접근하는 것을 막는 형태이다.
그렇다면 왜 외부에서 변경이 되는 것을 막는 것일까?
일단 뷰는 오로지 LiveData를 관찰하는 역할만 수행해야한다. 뷰모델이 LiveData를 관리하기 때문에 뷰모델에서만 LiveData의 값을 변경시키는 작업을 해야한다.
관심사 분리라고 하기에는 설득력이 부족하다. 뷰에서 LiveData를 수정할 수 있어도 같은 동작을 하기 때문이다. 즉, setter 함수를 따로 만들어 LiveData를 변경하나 프로퍼티에 직접 접근하여 값을 변경하나 똑같기 때문이다.
만약 LiveData에 값을 변경할 때 꼭 필요한 코드가 있다고 가정해보자. 예를 들어 음수 값이 담기면 안된다고 할 때 값이 양수인지 판단하는 코드가 필요하다. MVVM 구조는 1:N 관계이기 때문에 만약 해당 LiveData에 여러 뷰에서 접근한다고 할 때 양수인지 검증하는 코드가 중복해서 들어가게 된다. 그런 중복되는 코드를 ViewModel에서 하나의 setter 함수로 관리하게 되면 중복 코드를 없앨 수 있다.
만약 중복 코드가 없는 경우라면 값에 직접 접근해도 되는가? 를 생각해봤을 때 지금은 문제 없겠지만 추후 유지보수를 위해서라도 ViewModel에서 관리하는 것이 좋아보였다. Backing Property를 사용해야하는 이유에 대해서 확 와닿지는 않았지만 어느정도 정리가 된 것 같기도하다.
그 다음으로 생긴 의문점은 =
방식과 get()
방식이다. 여러 예제를 보면 두 방식 모두 사용된다. (sunflower에서도 혼용되어있다.) 어떤 방식을 사용하더라도 큰 의미는 없겠구나 생각은 했지만 두 방식의 차이를 알아보고 싶었다.
=
, get()
방식 차이get 방식에 대해서는 기존에 알고 있었다. 두 방식이 다르다는 것은 알고 있지만, ViewModel에서 사용되는 LiveData 자체가 변경되지 않고 LiveData로 감싸져있는 값이 변경되는 것이기 때문에 같은 동작을 한다고 생각했다.
그렇기 때문에! LiveData 자체는 변경되지 않는다는 것을 알고 있기 때문에! 의미상 get()
을 사용하는 것이 더 나을 수 있겠다고 생각했다. 왜냐면 get()
으로 정의되어있다는 것은 Mutable 데이터 자체의 값을 사용한다는 것을 직관적으로 알 수 있기 때문이다.
그렇게 생각하고 있던 중 자바 코드로 변환되면 어떤 차이가 있을 지 궁금해서 바로 시도해봤다.
private final MutableLiveData _setter;
@NotNull
private final LiveData setter;
private final MutableLiveData _getter;
@NotNull
public final LiveData getSetter() {
return this.setter;
}
@NotNull
public final LiveData getGetter() {
return (LiveData)this._getter;
}
public MainViewModel() {
this._setter = new MutableLiveData(id.home_fragment);
this.setter = (LiveData)this._setter;
this._getter = new MutableLiveData(id.home_fragment);
}
차이가 존재했다.
=
방식은 변수하나가 더 추가된다.
사실상 변수하나 추가되는 것이 그렇게 큰 메모리 영역을 차지한다고 생각하지 않기 때문에 어떤 것을 사용하여도 무방하다고 결론을 지었다. (나는 그래도 통일성을 위해 get()
방식을 사용하기로 했다.)