android

Firebase Ream-Time Database Paging 처리

android by kotlin 2022. 10. 25. 18:29

Firebase Real-Time Database 에서 데이터를 읽어와서 RecyclerView 또는 ViewPager2 에 데이터를 연결할 때 고려해야 하는 부분 중 서버에서 데이터를 한번에 읽어오지않고 사용자의 스크롤 지점에 따라서 조금씩 읽어오는 방식으로 하는 것이 좋습니다. 예를들어 서버에 저장된 Post 가 총 100개라고 하면 앱을 실행하면 최초로 10개만 받아오고 사용자가 스크롤하여서 앞으로 보여줄 아이템이 3~4개 정도 남아있을 때 다시 10개를 받아오는 방식으로 한다면 사용자에게 쾌적한 앱 서비스를 제공하는데 도움이 됩니다.

 

이 글에서는 Firebase Real-Time Database 를 사용하여 Paging 처리하는 방법에 대해 작성합니다.

 

1. 로딩한 데이터를 보여줄 ViewPager 를 초기화합니다.

private fun initTodayLogsViewPager() {
    binding.todayLogsViewpager.adapter = todayLogsAdapter
    binding.todayLogsViewpager.setPageTransformer(ZoomOutPageTransformer())
    binding.todayLogsViewpager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
        override fun onPageScrolled(
            position: Int,
            positionOffset: Float,
            positionOffsetPixels: Int
        ) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels)

            //현재까지 가져온 아이템의 마지막 인덱스 - (현재 보이는 아이템의 포지션 - 2) 일 때
            //즉 마지막 아이템 2개가 남았을 때 Firebase 에 추가 요청합니다.
            if(position == viewModel.todayLogs.value?.lastIndex?.minus(2)) {
                if(!viewModel.isFetching) {
                    viewModel.fetchMore()
                }
            }
        }
    })
    //최초로 보여줄 아이템을 요청합니다.
    viewModel.fetchDayLogs()
}

 

2. use case 를 호출하여 Firebase 에 데이터를 요청합니다.

fun fetchMore() {
    viewModelScope.launch {
        //동일한 아이템에 대해 여러번 호출되는 것을 방지하기 위한 플래그입니다.
        isFetching = true

        val index = _todayLogs.value?.lastIndex
        
        //Firebase 에 추가 데이터를 요청할 때 가장 마지막으로 받은 데이터의 key 값을 전달합니다.
        //이 key 값 이후의 값만 받아오기 위한 용도입니다.
         val response = fetchMoreDayLogsUseCase(todayLogs.value?.get(index!!)?.key.toString())
        _todayLogs.value?.let {
            val temp = it
            it.addAll(response)
            _todayLogs.value = temp
        }
        
        isFetching = false
    }
}

 

3. usecase 에서는 Firebase 에 데이터를 요청 후 받아오고 받아온 데이터를 앱에서 사용할 형태로 변환하여 반환합니다.

class FetchDayLogsUseCase {
    suspend operator fun invoke(): MutableList<DayLog> {
        val response = Repository.fetchDayLogs()
        val dayLogs = mutableListOf<DayLog>()

        for(item in response.children) {
            item.getValue(DayLog::class.java)?.let { dayLogs.add(it) }
        }

        return dayLogs
    }
}

 

4. Firebase Real-Time Database 에 데이터를 요청합니다.

orderByKey() 를 통해 우선 하위 노드들을 정렬합니다.  그리고 startAfter() 에 최근 가장 마지막으로 읽어온 데이터의 key 값을 넣어주면 그 이후의 데이터부터 읽어올 수 있습니다. 마지막으로 limitToFirst() 를 통해 몇개의 데이터를 읽어올 지 결장합니다.

suspend fun fetchMoreDayLogs(key: String): DataSnapshot {
    val response: DataSnapshot
    withContext(Dispatchers.IO) {
        Log.d("fetchMoreUseCase", key.toString())

        response = Firebase.database.reference.child(Constant.LOGS_NODE).child("2022-10-25").orderByKey().startAfter(key).limitToFirst(4).get().await()
    }
    return response
}