Android Weekly

Android Weekly #-587 "페이지네이션 라이브러리 Paging3"

베블렌 2023. 9. 18. 23:41

9월 2주차에는 여러 라이브러리들의 기본사용법과, 구글이 제작중인 앱에 관련한 글들이 올라왔습니다.

 

https://ayousuf.hashnode.dev/paging-3-on-android

 

Paging 3 on Android

An easy implementation guide for traditional UI and Jetpack Compose

ayousuf.hashnode.dev

 

 

소개

 

이번 글에서는 큰 데이터 세트를 효과적으로 표시하는 방법인 RecyclerView와 Paging 라이브러리의 이점을 소개합니다.


내용

 

1. Paging 라이브러리 이전의 상황:

  • RecyclerView만을 사용하여 페이지네이션을 구현했을 때의 어려움과 복잡성을 강조합니다. 이는 현재 페이지 상태 추적, 데이터 갱신 및 로딩 상태 표시와 같은 다양한 상태를 수동으로 관리해야 했기 때문입니다.

 

2. Paging 라이브러리의 소개:

 

공식문서에서 말하는 Paging의 이점으로는 아래와 같습니다.

  • Paging된 데이터의 메모리 내 캐싱. 이렇게 하면 앱이 Paging 데이터로 작업하는 동안 시스템 리소스를 효율적으로 사용할 수 있습니다.
  • 요청 중복 삭제 기능이 기본 제공되므로 앱에서 네트워크 대역폭과 시스템 리소스를 효율적으로 사용할 수 있습니다.
  • 사용자가 로드된 데이터의 끝까지 스크롤할 때 구성 가능한 RecyclerView 어댑터가 자동으로 데이터를 요청합니다.
  • Kotlin 코루틴 및 플로뿐만 아니라 LiveData 및 RxJava를 최고 수준으로 지원합니다.
  • 새로고침 및 재시도 기능을 포함하여 오류 처리를 기본으로 지원합니다.

 

페이징 라이브러리는 3계층으로 작동됩니다.

 

출처 : https://developer.android.com/static/topic/libraries/architecture/images/paging3-library-architecture.svg

 

3. Paging 3 라이브러리 사용 방법:

class NewsRemotePagingSource (private val topHeadlineService: TopHeadlineService) : PagingSource<Int, NewsArticle>() {
    override fun getRefreshKey(state: PagingState<Int, NewsArticle>): Int {
        return 1
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, NewsArticle> {
        val requestPage = params.key ?: 1
        return try {
            val response = topHeadlineService.getNews(page = requestPage)
            val body = response.body()
            if (response.isSuccessful && body != null) {
                val previousLoadCount = 0.coerceAtLeast(requestPage - 1) * networkPageSize
                val remainingCount = body.total - (previousLoadCount + body.articles.size)
                val nextPage = if (remainingCount == 0) {
                    null
                } else {
                    requestPage + 1
                }
                val prePage = if (requestPage == 1) null else requestPage -1
                LoadResult.Page(
                    data = body.articles,
                    prevKey = prePage,
                    nextKey = nextPage
                )
            } else {
                LoadResult.Error(Exception("Response body Invalid"))
            }
        } catch (ex : HttpException) {
            LoadResult.Error(ex)
        } catch ( ex: IOException) {
            LoadResult.Error(ex)
        }
    }

    companion object {
        const val networkPageSize = 20
        const val initialLoad = 20
        const val prefetchDistance = 2
    }
}

ㄴ 기본적인 Paging3 구현방식

  1. 데이터 소스 정의:
    • PagingSource를 구현한다.
    • 원하는 데이터 소스(서버, 로컬 데이터베이스 등)에서 load() 메서드를 오버라이드하여 데이터 로드한다.
  2. PagingData 스트림 설정:
    • ViewModel 내에서 Pager 클래스를 사용하여 PagingSource로부터 PagingData의 Flow를 설정한다.
  3. RecyclerView 어댑터와 UI 통합:
    • PagingDataAdapter를 확장하여 새 어댑터를 만든다.
    • RecyclerView에 해당 어댑터를 연결하고, 데이터를 어댑터에 제출한다.
  4. Jetpack Compose와의 통합:
    • Flow를 LazyPagingItems<T>로 변환하고 LazyColumn 내에서 아이템을 표시한다.

 

 

 

정리

 

Android 앱의 로컬 데이터베이스 관리 Room + 데이터 변경 감지인 Flow + 대량의 데이터 표시 Paging3 의 조합이 일반적인 패턴 중 하나입니다.
Paging3는 메모리 효율성, 리소스 최적화, 데이터 로딩 최소화로 인한 사용자 경험 개선 등의 장점이 있습니다.
현재 진행하는 프로젝트에서는 백엔드에서 paging을 처리하고 정해진 데이터량만 보내주는데, 개인적으로  모바일 프로젝트를 1인으로 만들어보고 싶은데, 그때 위에나온 패턴을 이용하면 재밌는 결과를 만들어 낼 수 있지 않을까 싶습니다.

 

 

 

 

 

https://androidweekly.net/

 

Android Weekly - Free weekly Android & Kotlin development newsletter

Android Weekly - Free weekly Android & Kotlin development newsletter

androidweekly.net