LiveData에서 Flow로 마이그레이션
RoomDB 객체 목록 가져오는 부분을 LiveData에서 Flow로 변경하여 데이터의 계층과 아키텍처 스타일 코드 준수
LiveData의 경우 UI와 밀접하게 연결되어 있어 UI가 아닌 다른 레이어에서 사용하기 적합하지 않았습니다. 그렇다고 LiveData를 사용하지 않고 Data를 가져오는 get 스타일의 코드를 작성한다면 MVVM 아키텍쳐가 뚜렷하게 구현되지 않을 것이라고 생각했습니다. 그래서 앱의 다른 레이어에서 코루틴 범위에서 작업을 실행하는 Flow로 수정하여 data 레이어와 UI를 분리시키고 MVVM 스타일에 맞는 코드를 지키려고 노력하였습니다.

개요
깃허브 북마크 앱을 개발하며 Room을 사용하여 로컬 데이터베이스에서 데이터를 가져올 때 UI 스레드와 밀접하게 연결되어 있는 LiveData를 지금까지 사용하고 있었습니다.
데이터 계층에 있는 Repository에 작성한 코드입니다. LiveData로 전체 데이터와 검색 데이터를 가져오는 코드가 구현되어있습니다. 이렇게 코드를 작성하게 된다면, Guide to app architecture의 아키텍처 설계 방법에서 데이터 모델에서 UI를 도출한다는 원칙을 지키지 못하게 됩니다.
데이터 모델에서 UI를 도출한다.
- 데이터 모델에서 UI를 도출해야 한다.
- 데이터 모델은 앱의 데이터를 나타내며, 앱의 UI 요소 및 기타 구성요소로부터 독립되야 한다.
- 즉, 이들은 UI 및 앱 구성요소 수명 주기와는 관련이 없다.
- 모델을 UI와 분리하여 앱의 수명 주기나 여러 사용자 환경 흐름에 영향을 받지 않도록 해야한다.
그래서 처음엔 get 스타일의 코드를 작성해 수동으로 업데이트 해주는 방식을 고민해보았습니다. 하지만, MVVM 아키텍쳐가 뚜렷하게 구현되지 않을 것이라고 생각이 되었고 다른 방법을 찾다 Flow를 알게 되었습니다.
Flow
Flow는 코루틴 범위에서 작업하는 데이터 스트림입니다.
스트림이란 데이터의 연속적인 흐름을 의미합니다. 데이터를 효율적이게 사용하기 위해 사용되는데 이 부분에서 LiveData와 비슷합니다. 스트림으로 데이터를 처리하게 된다면, 대용량 파일을 처리할 때 모든 데이터를 메모리에 불러오는 것보다 스트림을 사용하여 필요한 부분만 처리할 수 있습니다.
LiveData
안드로이드 생명주기를 인식하는 데이터 홀더 클래스로 데이터와 UI 동기화로 항상 최신 데이터를 유지할 수 있습니다.
LiveData가 항상 최신 데이터를 유지할 수 있는 이유는 Observer 객체가 데이터 변경을 감지하면, UI를 대신 업데이트 하기 때문입니다.
여기서 LiveData는 UI스레드에서만 값을 업데이트할 수 있다는 특징을 가지고 있는데 이러한 특징을 가지고 있는 LiveData를 데이터 모델에서 사용하게 된다면 UI요소를 사용하는것이 됩니다. 즉, LiveData는 비동기 데이터 스트림을 처리하도록 설계되지 않았습니다.
LiveData를 Flow로 변경
앱의 다른 레이어에서 데이터 스트림을 쓰기 위해서 Flow로 변경하였습니다.
override val wholeLikeUserData: Flow<List<LikeUserEntity>>
get() = likeUserDao.getLikeUser()
override suspend fun insertLikeUser(likeUserEntity: LikeUserEntity) {
likeUserDao.insertUser(likeUserEntity)
}
override suspend fun deleteLikeUser(likeUserEntity: LikeUserEntity) {
likeUserDao.deleteUser(likeUserEntity)
}
override fun searchLikeUserDB(searchQuery: String): Flow<List<LikeUserEntity>> {
return likeUserDao.searchDatabase(searchQuery)
}
그리고 UI 계층에서 해당 데이터를 LiveData로 사용할 수 있도록, viewModel에서 Flow를 asLiveData()를 써서 변환하였습니다.
val wholeLikeUserData: LiveData<List<LikeUserEntity>> =
likeUserRepository.wholeLikeUserData.asLiveData()
fun searchLikeUserDB(searchQuery: String): LiveData<List<LikeUserEntity>> {
return likeUserRepository.searchLikeUserDB(searchQuery).asLiveData()
}
fun insertLikeUserData(likeUserEntity: LikeUserEntity) {
viewModelScope.launch(Dispatchers.IO) {
likeUserRepository.insertLikeUser(likeUserEntity)
}
}
fun deleteLikeUserData(likeUserEntity: LikeUserEntity) {
viewModelScope.launch(Dispatchers.IO) {
likeUserRepository.deleteLikeUser(likeUserEntity)
}
}