Lazy Layout으로 대량 리스트를 효율적으로 렌더링하기
Lazy Layout으로 대량 리스트를 효율적으로 렌더링하기
Jetpack Compose에서 수백, 수천 개의 아이템을 화면에 표시해야 할 때, Column과 같은 일반 레이아웃은 화면에 보이는지 여부와 관계없이 모든 자식 컴포저블을 한꺼번에 컴포지션합니다. 이로 인해 불필요한 메모리 소비와 컴포지션 오버헤드가 발생하며, 결과적으로 UI 버벅임(jank) 현상이 나타날 수 있습니다. LazyColumn, LazyRow, LazyVerticalGrid와 같은 Lazy Layout은 현재 화면에 보이는 아이템만 컴포지션하고, 스크롤되어 화면 밖으로 나간 아이템은 폐기하는 방식으로 이 문제를 해결합니다. 이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.
- Lazy Layout이 일반 레이아웃과 컴포지션 동작 방식에서 어떻게 다른지 설명할 수 있습니다.
LazyColumn,LazyRow,LazyVerticalGrid를 활용하여 효율적인 리스트 렌더링을 구현할 수 있습니다.- 안정적인 키(key)를 적용하여 아이템 상태를 보존하고, 리스트 변경 시 불필요한 리컴포지션(Recomposition)을 최소화할 수 있습니다.
- Lazy Layout 사용 시 흔히 발생하는 성능 함정을 파악하고 이를 회피할 수 있습니다.
Lazy Layout의 동작 원리
1,000개의 자식을 가진 Column은 화면에 10개만 보이더라도 1,000개 아이템 모두를 즉시 컴포지션합니다. 각 아이템은 Composition, Layout, Drawing 페이즈를 거치며, 사용자에게 보이지 않는 콘텐츠를 위해 메모리와 CPU 자원을 소모하게 됩니다. 대량의 데이터를 표시해야 하는 실제 프로덕션 환경에서는 심각한 성능 저하의 원인이 될 수 있습니다.
LazyColumn은 이러한 문제를 아이템을 필요한 시점에만 컴포지션하는 방식으로 해결합니다. 사용자가 스크롤하면 새로운 아이템이 화면 영역에 들어올 때 컴포지션되고, 화면 영역을 벗어난 아이템은 폐기됩니다. 덕분에 전체 리스트 크기와 무관하게 컴포지션된 아이템 수가 대략 일정하게 유지됩니다.
@Composable
fun ItemList(items: List<Item>) {
LazyColumn {
items(items) { item ->
Text(
text = item.name,
modifier = Modifier.padding(8.dp)
)
}
}
}
위 코드에서 LazyListScope 내부의 items 함수는 각 아이템에 대한 팩토리를 등록합니다. 런타임은 해당 아이템이 화면에 보이게 될 때에만 이 팩토리를 호출하여 컴포지션을 수행합니다. 이러한 지연 컴포지션(lazy composition) 덕분에 초기 렌더링 속도가 크게 향상됩니다.
LazyRow를 활용한 수평 리스트
LazyRow는 수평으로 스크롤되는 콘텐츠에 동일한 지연 컴포지션 전략을 적용합니다.
@Composable
fun HorizontalItemList(items: List<Item>) {
LazyRow {
items(items) { item ->
Card(modifier = Modifier.padding(4.dp)) {
Text(text = item.name)
}
}
}
}