최근 Jetpack Compose와 Paging3를 활용한 프로젝트를 진행하면서 예상치 못한 문제가 발생했습니다. 바로 Paging3의 PagingDataAdapter와 Jetpack Compose의 LazyColumn을 함께 사용할 때 발생하는 충돌이었습니다. 이 글에서는 문제를 어떻게 해결했는지, 그 과정에서 겪었던 시행착오와 최종 해결 방안을 공유하려고 합니다.
먼저, PagingDataAdapter는 RecyclerView와 함께 사용하여 대용량 데이터를 효율적으로 표시할 수 있도록 도와주는 어댑터입니다. 이 어댑터는 RecyclerView와 긴밀하게 결합되어 있어, UI의 상태를 변경할 때 데이터를 자동으로 갱신하고, 데이터 변경 사항을 감지하여 UI를 업데이트합니다.
하지만 Jetpack Compose로 넘어오면서 문제가 발생했습니다. Jetpack Compose는 상태 기반의 UI 라이브러리로, 기존의 ViewGroup인 RecyclerView를 대체할 수 있는 LazyColumn 같은 컴포저블을 사용합니다. 문제는 PagingDataAdapter가 Compose 환경에서 제대로 동작하지 않는다는 점이었습니다.
LazyColumn과 PagingDataAdapter 간의 충돌:
PagingDataAdapter는 Compose의 LazyColumn과는 호환되지 않아, 데이터를 렌더링하거나 업데이트하는 과정에서 문제를 일으켰습니다.데이터 변경 감지의 어려움:
equals와 hashCode 메서드를 기반으로 상태를 감지하고, 상태가 변경되었을 때만 UI를 리렌더링합니다. 그런데 Paging3에서 제공하는 PagingData는 내부적으로 메모리 주소가 다르면 다른 객체로 인식되어, 같은 데이터를 가지고 있음에도 불구하고 불필요한 리렌더링이 발생할 수 있습니다.이번 문제의 근본 원인은 Paging3와 Jetpack Compose의 동작 방식의 차이에서 비롯되었습니다. 이 문제를 해결하기 위해 몇 가지 접근 방식을 시도해 보았습니다.
Flow의 재생성 문제:
RemoteMediator에서의 무한 호출 문제:
RemoteMediator를 사용해 원격 데이터를 페이징 처리할 때, 특정 검색어에 대해 데이터가 없을 경우, 무한 루프가 발생하는 문제가 있었습니다. 이는 RemoteMediator가 현재 페이지를 제대로 관리하지 못해 발생한 문제였습니다. Flow 상태 관리:
RemoteMediator에서의 페이지 관리:
lastRequestedPage 변수를 도입했습니다. 또한, 데이터가 없을 때 endOfPaginationReached 플래그를 설정하여 무한 호출 문제를 해결했습니다.왜 이러한 문제들을 Repository에서 처리해야 하는지 고민해 보았습니다.
데이터의 일관성:
관심사의 분리:
테스트 용이성:
Paging3와 Jetpack Compose를 통합하는 과정에서 Hilt를 활용하여 의존성 주입을 관리했습니다. 특히 RemoteMediator를 동적으로 생성할 수 있도록 Hilt 모듈을 설계하여 코드의 유연성을 확보했습니다.
Hilt를 사용해 PagingConfig와 RemoteMediator의 생성을 관리하였고, 이를 통해 Paging 관련 로직을 더 깔끔하게 분리할 수 있었습니다.
@Module
@InstallIn(SingletonComponent::class)
object PagingModule {
@Provides
@Singleton
fun providePagingConfig(): PagingConfig =
PagingConfig(
pageSize = 10,
prefetchDistance = 5,
enablePlaceholders = false
)
@Provides
fun provideRemoteMediatorFactory(
remote: BookRemoteDataSource,
local: BookLocalDataSource
): (String) -> BookRemoteMediator {
return { query -> BookRemoteMediator(query, remote, local) }
}
}
이번 프로젝트에서 Jetpack Compose와 Paging3를 결합하면서 예상치 못한 문제들을 많이 겪었지만, 이를 통해 많은 것을 배울 수 있었습니다. 특히, Jetpack Compose와 기존 UI 라이브러리의 차이점, Paging3의 동작 원리 등을 깊이 이해할 수 있었고, 문제 해결을 위해 아키텍처적으로 접근하는 방법을 습득했습니다.
이 글이 Jetpack Compose와 Paging3를 함께 사용할 때 발생할 수 있는 문제를 해결하는 데 도움이 되길 바랍니다. 추가적인 질문이나 논의가 필요하다면 언제든지 댓글로 알려주세요!