List — 무엇인가, SwiftUI의 목록 컴포넌트

저자: IT Sectr 게시일: 2026-06-25 읽는 시간: 7 분

List는 SwiftUI에서 데이터를 세로로 스크롤 가능한 목록으로 표시하기 위한 컨테이너 View로, UIKit의 UITableView와 유사합니다. Apple Developer Documentation, 2024에 따르면, List는 정적 및 동적 섹션, 스와이프 동작, 행 재정렬 및 당겨서 새로고침을 지원합니다. UITableView와 달리 List는 SwiftUI와 ForEach를 기반으로 하는 선언적 API를 사용하며, 셀 재사용과 많은 수의 행에 대한 성능을 자동으로 관리합니다.

핵심 요점

  • List — SwiftUI에서 스크롤 가능한 데이터 목록을 위한 컨테이너
  • ForEach — List에서 데이터를 동적으로 표시하는 기본 방법
  • 섹션 — 헤더로 행을 그룹화하는 Section View
  • 스와이프 동작 — iOS 15+용 swipeActions
  • 당겨서 새로고침 — iOS 15+용 .refreshable

SwiftUI에서 List란?

List는 항목의 시퀀스를 세로로 스크롤 가능한 목록으로 표시하는 View입니다. iOS 13에서 SwiftUI와 함께 도입되었으며, UIKit의 UITableView를 대체하는 데이터 목록 표시의 기본 방법입니다. List는 셀 재사용, 스크롤 및 성능을 자동으로 관리합니다.

List는 지연 로딩을 사용합니다: 셀은 스크롤할 때 생성되며 한 번에 모두 생성되지 않습니다. 이는 ScrollView 내부의 VStack 및 ForEach와 다르며, 여기서는 모든 셀이 렌더링 시 생성됩니다. List는 또한 스와이프 동작, 당겨서 새로고침, 편집(삭제/이동) 및 행 선택을 위한 기본 지원을 제공합니다.

Apple WWDC 2021 (Session 10072)에 따르면, iOS 15+의 List는 컬렉션 수준의 새로운 diffing 메커니즘 덕분에 상당한 성능 향상을 받았습니다. 이는 특히 수백 개의 행이 있는 목록의 데이터 업데이트 시 List를 더 효율적으로 만들었습니다.

List vs ScrollView + VStack

개발자는 View 세트를 표시하기 위해 List와 ScrollView + VStack 사이에서 선택하는 경우가 많습니다. 주요 차이점: List는 셀 재사용(UITableView와 유사)을 사용하는 반면, ScrollView + VStack은 모든 View를 즉시 생성합니다. 고정 크기 목록(최대 20개 항목)의 경우 차이가 미미합니다. 50개 이상의 행이 있는 동적 목록의 경우 성능 측면에서 List가 선호됩니다.

정적 및 동적 목록

정적 목록은 List 본문에 직접 지정된 고정된 수의 행을 가진 목록입니다. 메뉴, 설정 및 알려진 항목 집합이 있는 양식에 사용됩니다. 각 행은 루프나 ForEach 없이 명시적으로 선언됩니다.

swift
// 정적 목록(메뉴 및 설정용)
List {
    Text("프로필")
    Text("설정")
    Text("정보")
}

// 동적 목록(데이터용)
struct UserList: View {
    let users: [User]

    var body: some View {
        List(users) { user in
            HStack {
                Text(user.name)
                Text(user.role)
                    .foregroundColor(.secondary)
            }
        }
    }
}

동적 목록은 List(data:rowContent:) 이니셜라이저 또는 List 본문 내부의 ForEach를 사용합니다. 첫 번째 접근 방식은 각 행이 하나의 데이터 항목에 해당할 때 편리합니다. 두 번째는 데이터 사이에 섹션이나 추가 항목이 있을 때 유용합니다.

식별(Identifiable): 동적 목록의 경우 데이터 항목은 Identifiable 프로토콜을 준수해야 하거나 data:id 튜플에서 고유 식별자에 대한 KeyPath를 지정해야 합니다. SwiftUI는 식별자를 사용하여 추가, 삭제 및 행 이동을 추적합니다.

섹션 및 데이터 그룹화

Section은 List에서 헤더와 선택적 푸터로 행을 그룹화하는 View입니다. Section은 header와 footer를 ViewBuilder로 받아들여 섹션 헤더에 텍스트뿐만 아니라 사용자 정의 View를 사용할 수 있습니다.

swift
struct SettingsView: View {
    var body: some View {
        List {
            Section(header: Text("계정")) {
                Text("이름")
                Text("이메일")
            }
            Section(header: Text("알림")) {
                Toggle("Push", isOn: $pushEnabled)
                Toggle("Email", isOn: $emailEnabled)
            }
        }
        .listStyle(.insetGrouped)
    }
}

// ForEach를 사용한 동적 섹션
List {
    ForEach(groupedData.keys.sorted(), id: \.self) { key in
        Section(header: Text(key)) {
            ForEach(groupedData[key]!) { item in
                Text(item.title)
            }
        }
    }
}

List 스타일: SwiftUI는 .listStyle() 수정자를 통해 여러 내장 스타일을 제공합니다. .insetGrouped — iOS Settings의 표준, .plain — 미니멀, .inset — 들여쓰기 있음, .sidebar — iPad의 Sidebar용.

SwiftUI Cookbook (2024)에 따르면, 동적 섹션과 내부에 ForEach가 있는 Section은 복잡한 구조의 애플리케이션에서 데이터를 그룹화하는 표준 패턴입니다. 주요 규칙: Section을 Section 내부에 중첩하지 말고, Section과 함께 List(data:) 이니셜라이저를 사용하지 마세요 — List 본문 내부에서 ForEach를 사용하세요.

스와이프 동작 및 당겨서 새로고침

.swipeActions(edge:allowsFullSwipe:content:) — iOS 15+용 수정자로 List 행에 스와이프 동작을 추가합니다. 왼쪽(기본값) 또는 오른쪽으로 스와이프할 때 다양한 색상과 역할(destructive, cancel)로 버튼을 표시할 수 있습니다.

swift
struct TaskList: View {
    @Binding var tasks: [Task]

    var body: some View {
        List {
            ForEach($tasks) { $task in
                Text(task.title)
                    .swipeActions(edge: .trailing) {
                        Button("삭제", role: .destructive) {
                            tasks.removeAll { $0.id == task.id }
                        }
                    }
                    .swipeActions(edge: .leading) {
                        Button(task.isDone ? "실행 취소" : "완료") {
                            task.isDone.toggle()
                        }
                        .tint(.green)
                    }
            }
        }
        .refreshable {
            // 비동기 데이터 로딩
            await loadTasks()
        }
    }
}

.refreshable — iOS 15+용 수정자로 당겨서 새로고침을 추가합니다. 사용자가 목록을 아래로 당길 때 실행되는 async 클로저를 받습니다. SwiftUI가 자동으로 로딩 표시기를 표시합니다. 작업이 완료되면 표시기가 숨겨집니다.

.onDelete 및 .onMove — iOS 13+용 수정자로 행 삭제 및 재정렬 지원을 추가합니다. 이를 사용하려면 데이터를 Binding과 함께 ForEach로 감싸거나 List 또는 ForEach에서 .onDelete(perform:)를 통해 클로저를 전달하세요.

List 성능 및 최적화

List 성능은 행 수, 각 셀의 복잡성 및 데이터 업데이트 빈도에 따라 달라집니다. SwiftUI는 지연 로딩 및 셀 재사용(UITableView.dequeueReusableCell과 유사)을 사용하지만, 500개 이상의 행이 있는 목록의 경우 추가 최적화가 필요할 수 있습니다.

최적화설명iOS 버전
Identifiable각 항목의 고유 IDiOS 13+
EquatableView데이터가 같으면 다시 그리지 않음iOS 13+
id(_:)ID 변경 시 View 재생성 강제iOS 13+
.equatable()Equatable로 엄격한 비교iOS 15+
Diffable data변경 시 자동 diffiOS 15+

문제 1: 빈번한 업데이트. 목록의 데이터가 자주 업데이트되는 경우(예: 매초), List는 상태가 변경될 때마다 표시 셀을 다시 그릴 수 있습니다. 해결책: 데이터에 구조체(값 형식)를 사용하세요 — SwiftUI는 값을 비교하고 변경된 행만 다시 그립니다.

문제 2: 무거운 셀. 각 행에 복잡한 View 계층, 이미지 및 애니메이션이 포함된 경우 스크롤이 느려질 수 있습니다. 해결책: 셀을 별도의 View로 추출하고, EquatableView를 사용하여 불필요한 다시 그리기를 방지하세요. SwiftUI Lab (2024)에 따르면, 복잡한 행을 하위 구성 요소로 분할하면 렌더링 시간이 30~50% 감소합니다.

문제 3: 많은 수의 행. 1000개 이상의 행이 있는 경우 List는 지연 로딩 덕분에 여전히 효율적으로 작동하지만, 레이아웃 계산으로 인해 초기 로딩이 느려질 수 있습니다. 해결책: List 기능(스와이프, 섹션)이 필요하지 않은 균일한 행의 목록에만 LazyVStack을 사용하세요. 전체 기능이 필요한 목록의 경우 List가 최선의 선택입니다.

자주 묻는 질문

SwiftUI에서 List란?

List는 SwiftUI에서 스크롤 가능한 데이터 목록을 표시하기 위한 컨테이너 View입니다. 선언적 API를 가진 UIKit의 UITableView와 동일합니다. 섹션, 스와이프 동작, 당겨서 새로고침, 편집 및 .listStyle()을 통한 사용자 정의를 지원합니다.

List와 ScrollView + VStack의 차이점은?

List는 지연 로딩 및 셀 재사용을 사용합니다 — 스크롤할 때 셀이 생성됩니다. ScrollView + VStack은 모든 View를 한 번에 생성합니다. 50개 이상의 행이 있는 목록의 경우 List가 선호됩니다. 고정된 작은 집합(최대 20개 항목)의 경우 차이가 미미합니다.

List에 당겨서 새로고침을 추가하는 방법은?

.refreshable 수정자(iOS 15+)를 사용하세요. 데이터 업데이트 로직이 있는 async 클로저를 전달하세요. SwiftUI가 자동으로 로딩 표시기를 표시하고 async 작업이 완료된 후 숨깁니다.

List에서 행을 그룹화하는 방법은?

헤더와 선택적 푸터가 있는 Section View를 사용하세요. Section 내부에 목록 행을 배치하세요. 동적 섹션의 경우 groupedData와 함께 ForEach를 사용하세요. iOS와 같은 모양을 위해 .listStyle(.insetGrouped)로 목록 스타일을 구성합니다.

많은 수의 행이 있는 List를 빠르게 하는 방법은?

데이터에 구조체(값 형식)를 사용하고, EquatableView로 복잡한 셀을 별도의 View로 추출하고, 각 행에서 빈번한 상태 업데이트를 피하세요. 1000개 이상의 행이 있는 목록에서 List 기능이 필요하지 않은 경우 LazyVStack을 고려하세요.

요약

  • List — 지연 로딩 및 셀 재사용이 있는 스크롤 가능한 목록의 컨테이너
  • ForEach — List에서 데이터를 동적으로 표시하는 기본 방법
  • Section — 헤더 및 푸터로 행 그룹화
  • swipeActions — iOS 15+용 스와이프 동작
  • refreshable — iOS 15+용 당겨서 새로고침
  • 스타일 — .listStyle()을 통한 insetGrouped, plain, inset, sidebar
  • 성능 — Identifiable, EquatableView 및 값 형식 사용

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기