춤추는 개발자

[AAC] LiveData를 써야하는 이유? 본문

Android/study_til

[AAC] LiveData를 써야하는 이유?

Heon_9u 2021. 7. 6. 16:27
728x90
반응형

 

시작하기 전)

 얼마 전, IT직군 현직자들의 인터뷰를 접했다. 그 중, 눈에 띈 것은 모바일 직무에서 MVVM과 AAC를 활용한다는 점이었다. 기업에서 쓴다는 것은 그만큼 가치있는 기술이라 생각했고, 해당 기술들을 개인 프로젝트인 우산챙겨주는알림시계 활용하기로 결심했다.

 이때 당시, 날씨 탭 개발을 진행 중이였으나 과감하게 접었다. 해당 어플로 돈을 벌기보다 개발자로서의 경험과 역량 습득이 목적이었기 때문이다.

 

✅LiveData개요

LiveDataData의 변경을 관측할 수 있는 Data Holder 클래스입니다.
 일반적인 Observable과는 다르게 LiveData는 생명주기(Life Cycle)를 알고 있습니다. 즉, Activity, Fragment, Service 등과 같은 컴포넌트의 생명주기를 존중하고, 이에 따라 LiveData는 활성 상태(active)일 때만 데이터를 업데이트합니다.
 여기서 활성 상태란 STARTED 또는 RESUMED를 의미합니다.

 

 

LiveData의 장점

  • UI와 데이터 상태의 일치 보장
    • LiveData는 관찰자 패턴을 따릅니다. 기본 데이터가 변경될 때마다 Observer 객체에 알리고, 코드를 통합하여 Observer 객체의 UI를 업데이트할 수 있습니다.
  • 메모리 누수 없음
    • Observer는 LifeCycle 객체에 결합되어 있으며, 생명 주기가 끝나면 자동으로 삭제됩니다.
  • 수명 주기를 더 이상 수동으로 처리하지 않음
    • UI 구성요소는 관련 데이터를 관찰하기만 할 뿐, 관찰을 중지하거나 재시작하지 않습니다. LiveData는 관찰하는 동안 관련된 생명 주기 상태의 변경을 인식하므로 이 모든 것을 자동으로 관리합니다.
  • 최신 데이터 유지
    • 생명 주기가 비활성화되면 다시 활성화될 때, 최신 데이터를 수신합니다. 예를 들어, 백그라운드에 있었던 활동은 포그라운드로 돌아온 직후, 최신 데이터를 받습니다.
  • 적절한 구성 변경
    • 단말기 회전(세로 -> 가로)과 같은 변경으로 인해 Activity 또는 Fragment가 다시 생성되면 사용 가능한 최신 데이터를 즉시 받습니다.
  • 리소스 공유
    • 싱글턴 패턴을 사용하는 LiveData 객체를 확장하여 앱에서 시스템 서비스를 공유할 수 있습니다. LiveData 객체는 시스템 서비스와 한번 연결되면 자원이 필요한 모든 관찰자가 LiveData 객체를 볼 수 있습니다.

 

✅LiveData 객체 사용하기

1. 특정 유형의 데이터를 보유할 LiveData<T>의 인스턴스를 생성합니다. 해당 작업은 일반적으로 ViewModel 클래스 내에서 이루어집니다.

 

2. onChanged() 메서드를 정의하는 Observer 객체를 만듭니다. 해당 메서드는 LiveData 객체가 보유한 데이터 변경 시, 발생하는 작업을 제어합니다. 일반적으로 Activity나 Fragment 같은 UI 컨트롤러에 Observer 객체를 만듭니다.

 

3. observe() 메서드를 사용하여 LiveData 객체에 Observer 객체를 연결합니다. observe() 메서드는 LifeCycleOwner 객체를 사용합니다. 이렇게 하면 Observer 객체가 LiveData 객체를 구독하여 변경사항에 관한 알림을 받습니다. 일반적으로 Activtiy나 Fragment 같은 UI 컨트롤러에 Observer 객체를 연결합니다.

 

📢참고사항
 일반적으로 Activtiy와 Fragment와 같은 UI컨트롤러에서 LifeCycleOwner를 사용할 수 있습니다.
하지만, LifeCycleOwner 객체가 없는 경우, observeForever(Observer) 메서드를 사용하여 관찰자를 등록할 수 있습니다. 이 경우, 관찰자는 항상 활성으로 간주되며 항상 변경 사항에 관한 알림을 받습니다.
 마지막에는 removeObserver(Observer) 메서드를 호출하여 이러한 관찰자를 삭제할 수 있습니다.

 

 LiveData 객체에 저장된 값을 업데이트하면 연결되어있는 LifeCycleOwner가 활성 상태에 있는 한 등록된 모든 관찰자가 트리거됩니다.

 LiveData를 사용하면 UI 컨트롤러 관찰자가 업데이트를 구독할 수 있습니다. 즉, LiveData 객체는 보유한 데이터가 변경되면 UI가 자동으로 업데이트됩니다.

 

✅LiveData 객체 만들기

 LiveData는 Collections를 구현하는 List와 같은 객체를 비롯하여 모든 데이터와 함께 사용할 수 있는 Wrapper입니다. LiveData객체는 일반적으로 ViewModel 객체 내에서 저장되며, 다음 예에서 보는 것과 같이 getter 메서드를 통해 엑세스 됩니다.

 

public class NameViewModel extends ViewModel {

// Create a LiveData with a String
private MutableLiveData<String> currentName;

    public MutableLiveData<String> getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData<String>();
        }
        return currentName;
    }

// Rest of the ViewModel...
}

(해당 코드는 Android 가이드에 있습니다.)

 이외에 개인 프로젝트에서 작성했던 코드를 살펴보겠습니다.

 

public class AlarmViewModel extends AndroidViewModel {
    private AlarmRepository alarmRepository;
    private LiveData<List<Alarm>> allAlarms;

    public AlarmViewModel(@NonNull Application application) {
        super(application);
        alarmRepository = new AlarmRepository(application);
        allAlarms = alarmRepository.getAllAlarms();
    }
}

 

 

📢참고사항
 아래와 같은 이유로 Activity 또는 Fragment와 달리 ViewModel 객체의 UI를 업데이트하는 LiveData 객체를 저장해야합니다.

1. Activtiy와 Fragment가 지나치게 커지지 않게 하기 위함입니다. 이러한 UI 컨트롤러는 데이터 표시를 담당하지만, 데이터 상태를 보유하지 않습니다.
2. LiveData 인스턴스를 특정 Activity나 Fragment 인스턴스에서 분리하고, 구성 변경에도 LiveData 객체가 유지되도록 하기 위해서입니다.

 

 

✅LiveData 객체 관찰과 업데이트

 대부분의 경우, 앱 구성요소에서 처음 한번만 호출되는 onCreate() 메서드에서 LiveData 객체 관찰을 시작합니다. 이외에 생명주기 메서드에서 LiveData 객체 관찰을 시작할 경우, 중복 호출이 발생하게됩니다.

 또한, Activity와 Fragment에 활성 상태가 되는 즉시, 표시할 수 있는 데이터를 UI에 반영하기 위함입니다. 앱 구성요소는 STARTED 상태가 되는 즉시 관찰하고 있던 LiveData 객체에서 최신 값을 수신합니다. 이는 관찰할 LiveData 객체가 설정된 경우에만 발생합니다.

 

 일반적으로 LiveData는 데이터가 변경될 때, 활성 Observer에게만 업데이트를 전달합니다. 이 동작의 예외로, 관찰자가 비활성에서 활성상태로 변경될 때에도 Observer가 업데이트를 받습니다. 또한, 관찰자가 비활성에서 활성 상태로 두번 변경되면, 마지막으로 활성 상태가 된 이후, 값이 변경된 경우에만 업데이트를 받습니다.

 

 다음 코드는 LiveData 객체가 observe를 시작하는 방법입니다.

 

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    alarmViewModel = new ViewModelProvider(this).get(AlarmViewModel.class);
    alarmViewModel.getAllAlarms().observe(getViewLifecycleOwner(), new Observer<List<Alarm>>() {
        @Override
        public void onChanged(List<Alarm> alarms) {
            alarmAdapter.submitList(alarms);

            if(alarms.size() > 0) showAlarmList();
            else hideAlarmList();
        }
    });
}

 

 해당 코드에서는 Observer객체를 observe() 메서드에서 바로 생성하며 onChanged() 메서드를 오버라이딩했습니다.

처음 AlarmViewModel로 ViewModel 객체를 생성합니다. 이후, ViewModel에서 모든 객체가 담긴 리스트를 반환하는 getAllAlarms()를 호출합니다.

 마지막으로 observe()를 호출하여 관찰을 시작합니다. onChanged() 메서드의 내용을 살펴보면 alarmAdapter에 새로 갱신된 리스트인 alarms를 submit하여 데이터의 변경사항을 업데이트합니다. 또한, 리스트의 길이에 따라 알람 리스트를 보여주는 UI의 Visible관련 메서드를 호출하였습니다.

 

 

public void updateAlarmObject() {
    switch(requestCode) {
        case CREATE_ALARM_REQUEST:
            alarmViewModel.insert(alarm);
            Toast.makeText(context,
                    alarm.getHour() + "시 " + alarm.getMinute() + "분에 알람을 설정하였습니다.",
                    Toast.LENGTH_SHORT).show();
            break;
        case UPDATE_ALARM_REQUEST:
            alarmViewModel.update(alarm);
            Toast.makeText(context,
                    "알람을 수정했습니다.",
                    Toast.LENGTH_SHORT).show();
            break;
    }
}

 

 위 코드는 알람이 생성 또는 수정되는 경우, 호출하는 메서드입니다. alarmViewModel에서 Alarm Object가 insert 또는 update되는 경우, Observer에 의해 변경된 데이터가 업데이트 되며 UI까지 갱신하게 됩니다. (편의상 ViewModel관련 메서드는 제외하겠습니다.)

 

 LiveData에는 저장된 데이터를 직접 업데이트할 수 있는 메서드가 없습니다. 대신 MutableLiveData 클래스는 setValue(T)postValue(T) 메서드를 공개 메서드로 노출하여 LiveData 객체에 저장된 값을 수정할 수 있습니다. 일반적으로 MutableLiveData는 ViewModel에서 사용되며 ViewModel은 변경이 불가능한 LiveData 객체만 Observer에게 노출합니다.

 

📢참고사항
 LiveData 객체를 업데이트하는 경우, 기본 스레드에서는 setValue(T) 메서드를, 코드가 작업자 스레드에서 실행된다면 postValue(T) 메서드를 사용해야 합니다.

 

지금까지 LiveData에 대해 간단하게 알아보았습니다. 이외에도 LiveData의 확장과 자료형의 변환 등이 있지만, 직접 활용했던 부분만 첨부했습니다. 

 참고로 해당 포스팅에서 RecyclerView와 ListAdapter, ROOM, LiveData, ViewModel을 활용한 코드를 첨부했습니다. 그러다보니 제외된 코드들이 많아 해석이 어려우신 경우, Github 코드를 참고해주시기 바랍니다.

 

 

🙋‍♂️느낀점

 실제로 개인 프로젝트로 앱을 개발하면서 데이터의 변경 사항을 UI에 적용하기 위해 컴포넌트의 생명 주기 메서드를 자주 활용했습니다.

 하지만, 이제 LiveData와 ViewModel을 활용함으로써 이러한 작업들을 Observer가 대신할 수 있게 됐으며, 그만큼 코드가 간결해졌습니다. 기존 코드들을 리팩토링하는 과정이 쉽지 않았지만, 약 7일동안 고생하며 DataBinding, RxJava까지 적용한 결과, 앱의 유지보수성과 성능 개선을 한층 높일 수 있었습니다.

 

 단순한 기능 개발이 아닌 미래지향적인 코드를 짜는 것이 스마트한 프로그래밍이라는 것을 몸소 느꼈습니다. 조만간 Spring boot관련 개인 프로젝트를 진행하며 의미있는 코드를 짜는데 집중하겠습니다!!

 

 

 

728x90
반응형