0Pricing
Android Academy · 강의

페이징이 필요한 이유

모든 데이터를 한 번에 로드할 때 발생하는 비용을 알아봅니다.

페이징이 필요한 이유은(는) CoddyKit의 무료 Android Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Android Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

모든 데이터를 한 번에 불러오면 비용이 큽니다

50,000개의 항목이 있는 피드를 생각해 보세요. 모든 항목을 한 번에 가져와 보관하면 메모리를 낭비하고 네트워크 속도가 느려지며, 응답을 해석하는 동안 UI가 멈춥니다.

대부분의 사용자는 처음 몇 화면만 스크롤합니다. 페이징은 모든 데이터를 처음부터 불러오는 대신 사용자가 스크롤할 때 작은 단위(페이지)로 나누어 불러오는 방식입니다.

코드의 문제

순진한 접근 방식은 전체 목록을 메모리에 로드합니다. 서버가 이를 지원하더라도 응답이 매우 클 수 있으며, 파싱된 목록이 힙의 대부분을 차지합니다.

이 패턴은 확장성이 없고, 대규모 데이터셋에서는 OutOfMemoryError가 발생할 위험이 있습니다.

// Anti-pattern: load the whole table at once
suspend fun loadAllArticles(): List<Article> {
    // Could be tens of thousands of rows / megabytes of JSON
    return api.getArticles(limit = 50_000)
}

// UI holds ALL of them in memory at once
val articles = loadAllArticles()
LazyColumn {
    items(articles) { article -> ArticleRow(article) }
}

페이지의 모습

페이지는 데이터셋을 작게 나눈 일부로, 보통 20~50개의 항목으로 구성됩니다. 서버는 페이지 하나와 함께 다음 페이지를 가리키는 포인터(숫자, 오프셋 또는 토큰)를 반환합니다.

앱은 메모리에 몇 개의 페이지만 유지하고, 사용자가 스크롤하여 지나간 이전 페이지는 삭제합니다.

data class ArticlePage(
    val items: List<Article>,
    val nextKey: Int?   // null means no more pages
)

// Example REST call returning one page
suspend fun getArticlePage(page: Int, size: Int = 20): ArticlePage

Paging 3 살펴보기

Paging 3은 페이징의 어려운 작업을 모두 처리해 주는 Jetpack 라이브러리입니다.

  • 사용자가 목록 끝에 가까워지면 다음 페이지 요청
  • 메모리에 항목의 일부 구간만 유지
  • 로딩 및 오류 상태 제공
  • 코루틴, Flow 및 Jetpack Compose 기본 지원

종속 항목 추가

Paging 3은 별도의 아티팩트로 제공됩니다. 런타임과 Compose 통합 기능이 각각 있습니다. 모듈의 build.gradle.kts에 이를 추가합니다.

paging-compose 아티팩트는 LazyColumn을 위한 도우미를 제공합니다.

// build.gradle.kts (module)
dependencies {
    val pagingVersion = "3.3.6"
    implementation("androidx.paging:paging-runtime:$pagingVersion")
    implementation("androidx.paging:paging-compose:$pagingVersion")
}

세 가지 핵심 구성 요소

Paging 3은 다음 세 가지 타입을 중심으로 구성되며, 이 타입들을 반복해서 사용하게 됩니다.

  • PagingSource - 소스에서 페이지 하나를 로드하는 방법을 알고 있습니다.
  • Pager - 페이징된 데이터의 스트림을 설정하고 생성합니다.
  • PagingData - Flow로 방출되는 항목 컨테이너입니다.

다음 레슨에서 이 구성 요소를 각각 만들어 보겠습니다.

PagingData와 Flow

저장소는 Flow<PagingData<T>>를 노출합니다. UI는 이를 수집하고 현재 로드된 항목을 렌더링합니다.

전체 목록을 직접 구성할 필요는 없습니다. Paging이 필요할 때 항목을 UI로 스트리밍합니다.

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow

class ArticleRepository(private val api: ArticleApi) {
    fun articles(): Flow<PagingData<Article>> = Pager(
        config = PagingConfig(pageSize = 20)
    ) {
        ArticlePagingSource(api)
    }.flow
}

PagingConfig로 구간 제어하기

PagingConfig는 Paging이 데이터를 로드하고 보관하는 방식을 조정합니다.

  • pageSize - 페이지 요청당 항목 수
  • prefetchDistance - 다음 로드를 시작할 가장자리까지의 거리
  • enablePlaceholders - 아직 로드되지 않은 항목을 위한 자리 표시자 슬롯 표시 여부
  • maxSize - 메모리에 보관할 항목 수의 상한
import androidx.paging.PagingConfig

val config = PagingConfig(
    pageSize = 20,
    prefetchDistance = 5,
    enablePlaceholders = false,
    maxSize = 100
)

네트워크 또는 데이터베이스, 아니면 둘 다

PagingSource는 REST API, Room 데이터베이스 또는 임의의 사용자 지정 소스에서 데이터를 가져올 수 있습니다.

진정한 오프라인 우선 환경을 만들려면 RemoteMediator(마지막 레슨에서 다룹니다)를 사용해 두 가지를 결합합니다. 네트워크가 로컬 데이터베이스를 채우고, UI는 해당 데이터베이스에서 페이지를 로드합니다.

부드러운 스크롤, 적은 메모리

페이징을 사용하면 각 요청이 작고 메모리에 유지되는 구간이 maxSize로 제한되므로 스크롤이 부드럽게 유지됩니다.

로딩 및 오류 표시기는 LoadState를 통해 기본 제공되므로, 별도의 수동 관리 없이 하단에 로딩 표시기를 표시하거나 재시도 버튼을 추가할 수 있습니다.

// LoadState exposes loading/error per direction
when (val state = adapterLoadState.append) {
    is LoadState.Loading -> showBottomSpinner()
    is LoadState.Error -> showRetry(state.error)
    is LoadState.NotLoading -> hideBottomSpinner()
}

페이징이 필요한 경우

다음과 같은 경우 Paging 3을 사용합니다.

  • 데이터셋이 크거나 끝이 정해져 있지 않은 경우(피드, 검색 결과, 채팅 기록)
  • 페이지 또는 커서를 반환하는 네트워크 API에서 데이터를 가져오는 경우
  • 자동 로딩/오류 UI와 메모리 제어가 필요한 경우

짧고 고정된 목록(설정 메뉴 등)에는 일반 LazyColumn이 더 간단하며 충분히 적합합니다.

빠른 확인

대규모 목록에 Paging 3을 사용하는 가장 큰 이점은 무엇인가요?

복습: 페이징을 사용하는 이유

모든 데이터를 한 번에 로드하면 성능과 메모리에 어떤 문제가 생기는지, 그리고 Paging 3이 이를 어떻게 해결하는지 배웠습니다.

  • 사용자가 스크롤할 때 데이터가 작은 페이지 단위로 로드됩니다.
  • 세 가지 핵심 구성 요소는 PagingSource, Pager 및 PagingData입니다.
  • PagingConfig로 페이지 크기, 미리 가져오기 및 메모리 구간을 조정합니다.
  • Paging은 코루틴, Flow 및 Compose와 통합되며 로딩/오류 상태를 기본으로 제공합니다.

다음: PagingSource를 만들고 이를 Pager에 연결합니다.

자주 묻는 질문

“페이징이 필요한 이유” 강의는 무료인가요?

네 — “페이징이 필요한 이유” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Android Academy 강의 전체를 잠금 해제할 수 있습니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“페이징이 필요한 이유”에서 뭘 배우나요?

모든 데이터를 한 번에 로드할 때 발생하는 비용을 알아봅니다. 브라우저에서 직접 실행하는 실습 코드로 Android Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Android Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Android Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“페이징이 필요한 이유” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Android Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Android Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 페이징이 필요한 이유
  2. PagingSource와 Pager
  3. Compose 목록에서 페이징
  4. RemoteMediator와 캐싱
← Android Academy(으)로 돌아가기