LazyColumn은 Jetpack Compose에서 요소의 지연 로딩으로 세로 목록을 만들기 위한 컴포저블 함수입니다. Column과 달리 LazyColumn은 화면에 보이는 요소만 생성 및 구성하고 스크롤 시 재사용합니다. Google Android Documentation, 2025에 따르면 LazyColumn은 효율적인 메모리 관리 덕분에 모바일 애플리케이션에서 대규모 데이터 세트를 표시하는 기본 도구입니다.
핵심 사항
LazyColumn은 지연 로딩으로 세로 스크롤 가능 목록을 구현하는 컴포저블 함수입니다. 모든 하위 요소를 한 번에 렌더링하는 Column과 달리 LazyColumn은 뷰포트 내에 있는 요소만 생성합니다.
LazyColumn의 지연 로딩 메커니즘은 요소 재사용을 기반으로 합니다. 스크롤 중에 요소가 숨겨지면 해당 구성과 레이아웃을 새로 표시되는 요소에 재사용할 수 있습니다. 이는 특히 긴 목록에서 메모리와 CPU 시간을 절약합니다.
Android Developers Blog, 2024에 따르면 LazyColumn은 성능 저하 없이 수만 개의 요소로 구성된 목록을 효율적으로 처리할 수 있습니다. LazyColumn의 각 요소에는 목록이 변경될 때 Compose가 요소의 상태를 식별하고 유지하는 데 도움이 되는 키(key)가 있습니다.
LazyColumn은 modifier, state, contentPadding, reverseLayout, verticalArrangement 및 horizontalAlignment 매개변수를 받습니다. 콘텐츠는 DSL 함수 items와 item을 통해 람다 블록에서 정의됩니다.
@Composable
fun SimpleLazyColumn() {
val itemsList = (1..100).toList()
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(16.dp)
) {
items(itemsList) { item ->
Text(text = "항목 $item")
}
}
}
LazyColumn의 DSL 블록은 콘텐츠를 추가하기 위한 items와 item 함수를 제공합니다. items는 요소를 생성하기 위해 데이터 컬렉션, 키 및 팩토리 함수를 받습니다. item은 목록에 단일 정적 요소를 추가합니다.
itemsIndexed는 데이터 외에 요소 인덱스도 전달하는 items의 변형입니다. 이는 별도 계산 없이 요소 위치를 알아야 할 때 유용합니다. items(size)는 컬렉션에 바인딩하지 않고 요소 수와 팩토리만 받습니다.
items의 key 매개변수는 각 요소를 고유하게 식별합니다. Material Design Guidelines, 2025에 따르면 키는 요소 업데이트 및 애니메이션 시 성능을 향상시킵니다. Compose가 키로 특정 요소의 이동이나 변경을 추적할 수 있기 때문입니다.
contentType이 있는 items 함수는 LazyColumn이 다양한 유형의 요소 재사용을 최적화할 수 있게 합니다. 아래 예제에서는 하나의 목록에 섹션 헤더와 일반 행을 사용합니다.
@Composable
fun MixedItemsList() {
LazyColumn {
item {
Text(
text = "헤더",
style = MaterialTheme.typography.headlineMedium
)
}
items(
items = (1..20).toList(),
key = { it },
contentType = { "item" }
) { item ->
Text(text = "행 $item")
}
}
}
LazyListState는 LazyColumn의 상태 객체로, 현재 스크롤 위치, 첫 번째 보이는 요소 및 오프셋을 추적합니다. rememberLazyListState()를 통해 생성하고 LazyColumn의 state 매개변수에 전달할 수 있습니다.
LazyListState는 특정 요소로 프로그래밍 방식 스크롤을 위한 animateScrollToItem 및 scrollToItem 메서드를 제공합니다. animateScrollToItem은 부드러운 스크롤 애니메이션을 만들고 scrollToItem은 즉시 스크롤합니다. 두 메서드 모두 CompositionContext의 코루틴 내에서 호출해야 합니다.
Android Developers, 2024에 따르면 LazyListState에는 보이는 요소의 인덱스, 오프셋 및 크기에 대한 정보를 제공하는 layoutInfo도 포함되어 있습니다. 이는 분석, 조회 추적 또는 무한 스크롤 구현에 유용합니다.
이 예제에서는 LazyColumn이 LazyListState를 통해 제어됩니다. 버튼이 부드러운 애니메이션으로 목록을 인덱스 50의 요소로 스크롤합니다.
@Composable
fun ScrollableList() {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
Column {
Button(
onClick = {
scope.launch {
listState.animateScrollToItem(50)
}
}
) {
Text("50으로 스크롤")
}
LazyColumn(state = listState) {
items((1..100).toList()) { item ->
Text(
text = "항목 $item",
modifier = Modifier.padding(8.dp)
)
}
}
}
}
LazyColumn의 핵심 성능 요소는 키의 올바른 사용입니다. 각 요소는 key 매개변수를 통해 고유하고 안정적인 키를 가져야 합니다. 키가 없으면 Compose는 데이터 변경 시 요소를 올바르게 재사용할 수 없어 재구성이 발생합니다.
contentType은 LazyColumn에 어떤 요소가 동일한 레이아웃 유형인지 알려줍니다. 동일한 contentType의 두 요소가 재활용 풀에 들어가면 LazyColumn은 재구성 없이 레이아웃을 재사용할 수 있습니다. Android Developers Blog, 2024에 따르면 contentType은 다른 요소 유형이 있는 목록에서 성능을 30~50% 향상시킵니다.
LazyColumn의 경우 팩토리 함수에서 불안정한 매개변수를 피하는 것도 중요합니다. 요소 매개변수가 변경될 때마다 Compose가 해당 요소를 재구성합니다. 안정적인 값에 remember와 derivedStateOf를 사용하면 재구성 횟수가 줄어듭니다.
| 요소 | 영향 | 권장사항 |
|---|---|---|
| 키 (key) | 중요 | items에 항상 고유하고 안정적인 key를 설정하세요 |
| contentType | 상당함 | 재사용 최적화를 위해 요소 유형을 지정하세요 |
| 요소 크기 | 보통 | 가능하면 고정 크기를 사용하세요 |
| 중첩 목록 | 중요 | LazyColumn 내부에 LazyColumn을 피하세요 — LazyVerticalGrid를 사용하세요 |
stickyHeader는 LazyColumn의 DSL 함수로, 스크롤 중에 섹션 헤더를 목록 상단에 고정합니다. 다음 섹션이 상단에 도달하면 해당 헤더가 현재 헤더를 대체합니다. 이 동작은 연락처 목록과 달력에서 사용자에게 친숙합니다.
stickyHeader는 item과 유사하게 키와 팩토리 함수를 받습니다. 일반 item과 달리 stickyHeader는 섹션 콘텐츠를 스크롤하는 동안 계속 표시됩니다. 섹션이 완전히 스크롤되면 다음 섹션의 헤더가 이전 헤더를 밀어냅니다.
Material Design Guidelines, 2025에 따르면 sticky header는 긴 목록에서 탐색을 개선하고 사용자의 인지 부하를 줄입니다. sticky header를 구현하려면 items 요소에 대해 올바른 위치에서 섹션 헤더를 stickyHeader로 감싸기만 하면 됩니다.
@Composable
fun SectionedList(sections: Map<String, List<String>>) {
LazyColumn {
sections.forEach { (header, items) ->
stickyHeader {
Text(
text = header,
modifier = Modifier.background(Color.White)
.padding(16.dp)
)
}
items(items) { item ->
Text(text = item, modifier = Modifier.padding(8.dp))
}
}
}
}
자주 묻는 질문
Column은 모든 요소를 한 번에 렌더링하며 최대 20~30개 요소 목록에 적합합니다. LazyColumn은 보이는 요소만 생성하고 스크롤 시 재사용하므로 성능 저하 없이 수천 개의 요소로 작업할 수 있습니다.
verticalArrangement 매개변수에서 Arrangement.spacedBy를 사용하거나 인덱스를 통해 구분선이 있는 items 함수를 사용하세요. 복잡한 구분선의 경우 목록 요소 사이에 Divider()가 있는 별도의 item을 추가하세요.
LazyColumn은 안정적인 키가 사용되는 경우 데이터 컬렉션이 변경될 때 자동으로 재구성됩니다. mutableStateListOf 또는 StateFlow를 통해 감지된 데이터 변경으로 인해 변경된 요소만 다시 그려집니다.
LazyColumn 안에 LazyColumn을 직접 중첩하는 것은 권장되지 않습니다. 둘 다 독립적인 스크롤이 있어 제스처 충돌이 발생합니다. 대신 스크롤 가능한 Section 내에 LazyColumn이 있는 일반 Column이나 그리드에는 LazyVerticalGrid를 사용하세요.
LazyListState.layoutInfo.visibleItemsInfo를 사용하고 마지막 보이는 요소의 인덱스를 전체 요소 수와 비교하세요. 무한 스크롤의 경우 마지막 보이는 요소가 목록 끝에 가까워지는 시점을 추적하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.