본문 바로가기

안드로이드/코틀린

[Android/Kotlin] Transformations를 사용해 Livedata 활용하기

반응형

Livedata는 양방향 데이터 바인딩이나 Transformation과 함께 쓸수록 더 유용하게 사용할 수 있습니다. 

Transformation은 하나의 Livedata가 변경될때마다 다른 Livedata를 변경해줄때 사용하면 유용합니다.

>> Transformations.map ( 공식문서

  • 첫번째 인자로 Livedata 넘겨준다.
  • 두번째 인자 리턴 형태가 있는 함수를 넘겨주는데 반환 타입에 제한은 없다 ( 리턴된 타입이 String이면 Livedata<String>으로 반환된다.)
  •  
    var name = MutableLiveData<String>()
    var displayName = Transformations.map(name){
        mapName ->
        "우리${mapName}"
    }

디비에 저장된 이름을 불러오는 'name' 변수가 있고, 그 값을 가지고 화면에 뿌려줄땐 접두사로 '우리' 를 붙인다고 가정하면 위 처럼 코딩할 수 있습니다. 'mapName'name 이 변경될때마다 호출됩니다. Activity에서 라이브데이터를 observe 하는 개념과 동일하다고 보시면 되는데 return값이 MutableLivedata가 아니기 때문에 사용시 주의하셔야 합니다.

또 다른 예로 디비 내부에 식물 리스트를 관리하는 테이블과 식물별 일기 테이블이(foreign key가 식물 아이디) 있을때,  Activity에선 유저가 식물을 선택할때 마다 그 식물의 다이어리 목록도 Activity에 보여줘야 하는 경우

map 을 사용하지 않았을땐 Activity에서 ViewModel의 식물 아이템을 Observe하고 있다가 갱신될때 ViewModel에 다이어리를 가져오는 함수를 실행 시켜 비효율적이였습니다.

activity(식물 선택) -> viewModel(식물 정보 갱신 ) activity ( 갱신된 식물 정보로 다이어리를 가져오도록 함수 호출) -> viewModel ( 다이어리 정보 갱신 ) -> activity ( 갱신된 다이어리 정보를 보여준다)

map 을 사용하면 식물이 변경되는 시점에 다이어리도 가져오기 때문에 보다 클린한 코딩이 됩니다.

 viewModel(식물) -> viewModel(다이어리) -> activity( 다이어리 리스트 보여주기 )

map 적용 전 코드

ViewModel.kt

    private var plantItems = MutableLiveData<PlantData.Item>()
    private var diaryItems = MutableLiveData<DiaryData.Item>()
    
    fun getDiary(plantId: Long){
        diaryItems.value = repo.getDiaryList(plantId)
    }
    
Activity.Kt

    viewModel.plantItems.observe(this, Observer { 
        viewModel.getDiary(it.id)
    })

    viewModel.diaryItems.observe(this, Observer {
         // 다이어리 리스트 보여주기 
     })

   

map 적용 후 코드

ViewModel.kt

    var plantItems = MutableLiveData<PlantData.Item>()
    var diaryItems = Transformations.map(plantItems){
        repo.getDiaryList(it.id)
    }

Activity.kt

    viewModel.diaryItems.observe(this, Observer {
        // 다이어리 리스트 보여주기
    })

코드 라인수의 차이는 크게 없으나 훨씬 덜 복잡한걸 알 수 있습니다. ViewModel에서 Activity에 직접 접근해 다이어리 리스트를 접근 해주는 방법도 있지만 Mvvm과는 어긋나는 방식입니다. 

>> Transformations.switchMap

map 과 동일하게 첫번째 인자는 라이브데이터를 두번째 인자는 함수를 넘겨주지만 다른 점은 리턴 타입이 Livedata 여야 한다. 그렇기 때문에 Livedata 를 반환하는 작업에 많이 쓰인다. ( Room 데이터 select ) 

>> MediatorLiveData 

두개 이상의 Livedata 를 관찰하여 변경된 시점에 결합된 하나의 데이터를 만들때 사용하면 유용합니다.

예로 '식물 일기' 앱에서 식물을 선택했을때 와 날짜를 변경했을때 각 일기 목록을 내부 디비에서 가져올때

위에서 언급한 map 을 사용해서 식물 선택시 가져온 다이어리는 MutableLivedata 가 아니기 때문에 변경해줄 수 없습니다. 그렇기 때문에 날짜 선택시 변경되는 Livedata 를 새로 만들고, 2개의 Livedata 를 관찰할 MediatorLivedata 를 만들어서 Activity에선 MediatorLivedata 하나만 관찰하는 구조입니다.

사실 해당 경우는 2가지 선택 사항이 있습니다.

  • 두개의 Livedata 를 ViewModel에 각각 선언하고 관찰하여 변경된 시점에 Activity에서 다이어리 아이템을 변경한다
  • MediatorLivedata 를 사용하여 하나를 관찰하여 변경한다. 
ViewModel.kt
    var mDiaryList = MediatorLiveData<List<DiaryData.Item>>()
    
    mDiaryList.addSource(diaryList) {
        mDiaryList.value = it
    }

    mDiaryList.addSource(plantDiaryList) {
        mDiaryList.value = it
    }
    
Activity.kt

    viewModel.mDiaryList.observe(this, Observer { value ->
        diaryAdapter.selectPlantId = viewModel.selectPlant.value?.id ?: 0L
        diaryAdapter.items = value ?: listOf()
    })

     
   

위 코드는 2번째 방법으로 구현한 코드 입니다. 두개의 Livedata 를 결합하여 사용하진 않지만 Activity에서 ViewModel에 의존성을 줄여줄 수 있는 코드입니다. 

이전엔 하나의 Livedata 를 두고 변경해주는 방식으로 작성했는데 Transformations 를 활용하니 조금 더 클린한 코드가 됐습니다. 

 

위 내용은 제가 mvvm을 공부하고 적용하는 과정에서 작성된 코드로 잘못된 부분이나 조금 더 나은 방법이 있으면 댓글로 남겨주세요! 참고해서 반영하도록 하겠습니다. 감사합니다

 

 

 

 

반응형