대량 데이터 처리
한 번에 모든 데이터를 가져오지 않고, 필요한 만큼만 불러와서 메모리 사용을 최적화
자동 로드 & 페이징 처리
리스트의 끝에 도달하면 자동으로 다음 페이지 데이터를 가져올 수 있음
RecyclerView + DiffUtil 지원
데이터 변경 시 전체 리스트가 아니라 변경된 부분만 업데이트해서 성능을 높여.
Flow & LiveData 지원
MVVM 패턴과 잘 어울려서 Jetpack 구성요소와 함께 사용하기 좋다.
Paging 3 기본 구조
Paging3 에는 데이터를 어떻게 로드할 것인지 정의하는 PagingSource, 데이터를 관리하는 Pager, 화면에 표시하는 PagingDataAdapter가 존재한다
PagingSource
class ChatPagingSource @Inject constructor(
private val repository: Repository,
private val chatGroup: String
) : PagingSource<Int, Chat>() {
private companion object {
const val INIT_PAGE_INDEX = 0
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Chat> {
val pageNumber = params.key ?: INIT_PAGE_INDEX
return try {
val chat = repository.getChats(chatGroup, pageNumber, params.loadSize)
val prevKey = if (pageNumber == INIT_PAGE_INDEX) null else pageNumber -1
val nextKey = if (chat.isNullOrEmpty()) null else pageNumber + 1
LoadResult.Page(
data = chat,
prevKey = prevKey,
nextKey = nextKey
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Int, Chat>): Int? {
return null
}
}

pager
fun getChats(chatGroup: String) : Flow<PagingData<Chat>> {
return Pager(config = PagingConfig(pageSize = 20, enablePlaceholders = false, initialLoadSize = 20, ), pagingSourceFactory = {
ChatPagingSource(repository, chatGroup)
}).flow
}
PagingDataAdapter
class ChatAdapter() : PagingDataAdapter<Chat, ChatHolder>(CHAT_DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatHolder =
ChatHolder( // view 홀더 생성
ChatItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ChatHolder, position: Int) {
val item = getItem(position)
if(item != null) {
holder.setData(item) // 데이터 바인딩
}
}
companion object {
private val CHAT_DIFF_CALLBACK = object : DiffUtil.ItemCallback<Chat>() {
override fun areItemsTheSame(oldItem: Chat, newItem: Chat): Boolean {
// Id is unique.
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Chat, newItem: Chat): Boolean {
return oldItem == newItem
}
}
}
}
class ChatHolder(private val binding: ChatItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun setData(chat: Chat) {
// mapping
}
PagingSource → API에서 데이터를 불러오는 로직 정의