본문 바로가기

Android Library

[Android Library] Paging (1) - 대규모 데이터

안녕하세요.

 

안드로이드 Jetpack Library 중 하나인 Paging에 대해 알아보겠습니다.


Paging?

대용량 데이터(로컬 데이터베이스 or 서버 데이터)를 페이지 별로 로드하고 표시할 수 있게 해주는 라이브러리 입니다.

 

데이터를 한번에 전부 다 들고오는 것이 아닌,

일정 부분만 들고온 뒤에 필요할 때마다 다음 일정 부분을 들고오게 해줍니다.

 

대표적으로 구글 검색 엔진이 있습니다.


장점

대용량 데이터를 처리하는 앱에서 모든 데이터를 한 번에 로드하면 아래와 같은 문제점이 발생합니다.

  • 메모리 부족 문제
  • 초기 로딩 시간이 길어짐

 

Paging을 쓰면 위와 같은 문제를 해결할 뿐만 아니라, 아래와 같은 장점들도 얻을 수 있습니다.

  • 메모리 관리
    • 현재 화면에 보이는 데이터만 로드하기 때문에 메모리 누수를 방지함
  • 빠르고 부드러운 스크롤
    • 스크롤 할 때 필요한 데이터만 로드되므로 스크롤이 더 부드럽고 빠름
  • 무한 스크롤
  • 네트워크 트래픽 관리

구성요소

안드로이드 공식 문서에서는 아래와 같이 레이어를 나누고 그 안에 여러 요소들을 정의했습니다.

PagingSource

특정 페이지 쿼리의 데이터 청크를 로드하는 기본 클래스입니다.

데이터 소스 및 소스에서 데이터를 가져오는 방법에 대해 정의합니다.

Data Layer에 속합니다.

 

class ArticlePagingSource : PagingSource<Int, Article>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {
        ...
				return LoadResult.Page(
            data = data,
            prevKey = params.key?.minus(1),
            nextKey = params.key?.plus(1)
        )
    }
    override fun getRefreshKey(state: PagingState<Int, Article>): Int? {
        val anchorPosition = state.anchorPosition ?: return null
        val article = state.closestItemToPosition(anchorPosition) ?: return null
        return ensureValidKey(key = article.id - (state.config.pageSize / 2))
    }
}

<Key, Value> 형태

Key : 데이터를 로드하는데 사용하는 식별자 번호

Value : 데이터 클래스 

 

Override Function

  • load
    • 특정 페이지에 대한 데이터를 검색하고 반환하는 역할을 합니다.
    • prevKey, nextKey로 이전 페이지, 다음 페이지를 연결시킵니다.
  • getRefreshKey
    • UI 관련 항목을 새로고침해야 할 때 호출합니다.
    • 새로고침하면 -> PagingSource의 데이터가 변경 -> PagingSource를 새로 만들고 -> PagingData를 내보내 UI에 알리게 되는 구조입니다.

PagingConfig

페이징 동작을 결정하는 매개변수를 정의하는 클래스입니다.

페이지 크기, 자리표시자의 사용 여부 등을 지정할 수 있습니다.

자리표시자
- 임시로 채워넣는 내용물
- 데이터 로드 전 로딩 중 등과 같은 것

 

class ArticleViewModel(
    repository: ArticleRepository,
) : ViewModel() {

    val items: Flow<PagingData<Article>> = Pager(
        config = PagingConfig(pageSize = ITEMS_PER_PAGE, enablePlaceholders = false),
        pagingSourceFactory = { repository.articlePagingSource() }
    )
        ...
}

PagingConfig 클래스로 pageSize와 enablePlaceholders를 설정할 수 있습니다.

이 밖에 다른 options 들도 설정해줄 수 있습니다.


Pager

PagingData 스트림을 생성하는 클래스입니다.

=> Flow를 통해 UI에 전달합니다.

 

아래와 같은 이유로 ViewModel에서 만드는 것을 권장합니다.

  • 라이프 사이클 관리
    • UI 라이프 사이클과 연결되어 있어서 데이터를 관리하기 좋은 위치
  • 구성 변경 시 데이터 보존
    • 구성 변경 (화면 회전 등) 에도 데이터를 보존해준다는 장점

 

class ArticleViewModel(
    repository: ArticleRepository,
) : ViewModel() {

    val items: Flow<PagingData<Article>> = Pager(
        ...
    )
        .flow
        .cachedIn(viewModelScope)
}

Pager 클래스와 flow를 활용해 viewModel에서 데이터 스트림을 만들어 UI에 전달합니다.


PagingData

페이지로 나눈 데이터의 컨테이너입니다.

데이터를 새로고침할 때마다 자체 PagingSource로 지원되는 상응하는 PagingData 내보내기가 별도로 생성됩니다.

 

class ArticleViewModel(
    repository: ArticleRepository,
) : ViewModel() {

    val items: Flow<PagingData<Article>> = Pager(
        ...
    )
    
    ...
}

 

Article 데이터를 감싸는 데이터 컨테이너 입니다.

PagingData<Article>로 쓰인 부분을 확인할 수 있습니다.


PagingDataAdapter

RecyclerView에 PagingData를 표시하는 RecyclerView.Adapter 서브 클래스입니다.

내부 PagingData 로드 이벤트를 수신 대기하고, 페이지가 로드될 때 UI를 효율적으로 업데이트 합니다.

 

class ArticleAdapter : PagingDataAdapter<Article, ArticleViewHolder>(ARTICLE_DIFF_CALLBACK) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder =
        ArticleViewHolder(
            ArticleViewholderBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false,
            )
        )

    override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
        ...
    }
}

 

RecyclerView에 바인딩하기 위해 Adapter를 사용합니다.

ViewHolder를 만들고 바인드 하는 함수 두개를 오버라이드해서 쓸 수 있습니다.


여기까지 Paging에 대한 기본적인 것을 알아봤습니다.

다음 글에서는 코드와 함께 사용 방법에 대해 알아보겠습니다.


Reference

Android Paging 기본사항  |  Android Developers

 

Android Paging 기본사항  |  Android Developers

이 Codelab에서는 목록을 표시하는 앱에 Paging 라이브러리를 통합합니다. Paging 라이브러리를 사용하면 로컬 저장소에서나 네트워크를 통해 대규모 데이터 세트의 데이터 페이지를 로드하고 표시

developer.android.com