면접 질문 목록으로 가기
면접 질문실전 질문꼬리 질문

RecyclerView를 활용한 대용량 데이터셋 페이징

skydovesJaewoong Eum (skydoves)||7분 소요

RecyclerView를 활용한 대용량 데이터셋 페이징

페이징(paging) 시스템은 대용량 데이터셋을 다룰 때, 데이터를 로드하고 화면에 표시하는 방식을 최적화하는 기법입니다. 작고 관리하기 쉬운 단위(chunk)로 나누어 데이터를 가져오기 때문에, 앱의 성능을 안정적으로 유지하면서 더 나은 사용자 경험을 제공할 수 있습니다. 모든 데이터를 한꺼번에 로드하면 메모리 소비가 급격히 증가하고, 초기 로딩 시간이 길어지며, 네트워크 대역폭도 불필요하게 낭비됩니다. 실제 프로덕션 환경에서는 수천, 수만 건의 데이터를 한 번에 가져오는 것이 거의 불가능에 가깝기 때문에, 페이징은 선택이 아닌 필수 패턴이라 할 수 있습니다.

이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.

  • 페이징 없이 대용량 데이터셋을 로드하면 왜 성능 문제가 발생하는지 설명할 수 있습니다.
  • RecyclerView의 스크롤 리스너를 활용하여 수동 페이징 시스템을 직접 구현할 수 있습니다.
  • ListAdapterDiffUtil이 어떻게 협력하여 리스트를 효율적으로 업데이트하는지 이해할 수 있습니다.
  • 수동 페이징과 Jetpack Paging 라이브러리를 각각 어떤 상황에서 사용해야 하는지 판단할 수 있습니다.

페이징이 중요한 이유

데이터를 작은 페이지 단위로 나누어 로드하면, 메모리 사용량이 크게 줄어들어 메모리 부족(Out of Memory) 오류를 사전에 방지할 수 있습니다. 현재 화면에 보이는 영역(viewport)에 필요한 데이터만 먼저 가져와서 렌더링하므로, 초기 로딩 속도도 대폭 빨라집니다. 또한, 추가 데이터는 사용자가 실제로 필요로 할 때만 요청하기 때문에 네트워크 사용량을 최소화할 수 있으며, 이는 네트워크 환경이 좋지 않은 상황에서 특히 큰 이점을 제공합니다.

사용자 경험 관점에서 보면, 페이징 시스템은 사용자가 리스트를 탐색하는 동안 동적으로 데이터를 로드하므로, 부드러운 스크롤을 구현할 수 있습니다. 무한 스크롤(infinite scrolling)이나 대규모 데이터 소스를 다루는 앱에서 이 방식을 적용하면, 디바이스나 네트워크에 과도한 부하를 주지 않으면서도 반응성이 뛰어난 인터페이스를 구현할 수 있습니다. 면접에서 이 주제가 나오면 단순히 "데이터를 나눠서 로드한다"는 수준을 넘어, 메모리, 네트워크, UX 세 가지 측면에서 구체적으로 설명하시면 좋은 인상을 남기실 수 있습니다.

DiffUtil을 활용한 어댑터 구성

첫 번째 단계는 ListAdapter를 사용하여 RecyclerView.Adapter를 만드는 것입니다. ListAdapterDiffUtil을 통해 리스트 비교(diff) 연산을 자동으로 처리하므로, 새로운 페이지가 추가될 때 전체 리스트를 새로고침하지 않아도 됩니다.

class PokedexAdapter :
    ListAdapter<Pokemon, PokedexAdapter.PokedexViewHolder>(diffUtil) {

    override fun onCreateViewHolder(
        parent: ViewGroup, viewType: Int
    ): PokedexViewHolder {
        val binding = ItemPokemonBinding.inflate(
            LayoutInflater.from(parent.context)
        )
        return PokedexViewHolder(binding)
    }

    override fun onBindViewHolder(
        holder: PokedexViewHolder, position: Int
    ) { /* bind data */ }

    inner class PokedexViewHolder(
        private val binding: ItemPokemonBinding
    ) : RecyclerView.ViewHolder(binding.root)

    companion object {
        private val diffUtil =
            object : DiffUtil.ItemCallback<Pokemon>() {
                // 같은 아이템인지 고유 식별자(name)로 판별
                override fun areItemsTheSame(
                    oldItem: Pokemon, newItem: Pokemon
                ) = oldItem.name == newItem.name

                // 아이템의 실제 내용이 동일한지 비교
                override fun areContentsTheSame(
                    oldItem: Pokemon, newItem: Pokemon
                ) = oldItem == newItem
            }
    }
}

이 면접 질문은 구독자 전용입니다

Dove Letter를 구독하시면 안드로이드, 코틀린 개발 관련 독점 면접 질문의 전체 내용을 볼 수 있습니다.

구독하기
면접 질문 목록으로 가기