SwiftUI List와 ScrollView + LazyVStack 심화 비교 및 고급 사용법

작성일 :

SwiftUI List와 ScrollView + LazyVStack 심화 비교 및 고급 사용법

이 글에서는 SwiftUI List와 ScrollView + LazyVStack의 성능 비교, 고급 사용법, 그리고 실용적인 예제를 통해 각 방법의 장단점을 심도 있게 분석합니다. 이를 통해 어떤 상황에서 어떤 방법을 선택해야 할지 명확하게 이해할 수 있습니다.

1. 성능 비교

메모리 사용

LazyVStack의 지연 로딩(Lazy Loading)은 성능 최적화에 큰 장점이 있습니다. 이는 스크롤하는 동안 필요한 뷰만 메모리에 로드하므로, 초기 로딩 시간이 짧고 메모리 사용량이 적습니다. 반면, List는 셀 재사용 메커니즘을 통해 메모리 사용을 최적화하지만, 많은 데이터가 한 번에 로드될 때 성능 저하가 발생할 수 있습니다.

swift
struct LazyListView: View {
    let items = Array(1...1000).map { "Item \($0)" }

    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(items, id: \.self) { item in
                    Text(item)
                }
            }
        }
    }
}

큰 데이터 세트에서의 성능

큰 데이터 세트를 처리할 때는 ScrollView + LazyVStack이 더 유리합니다. LazyVStack은 필요한 아이템만 로드하므로, 스크롤이 부드럽고 메모리 사용이 최적화됩니다. List는 데이터가 많을수록 셀 재사용을 통해 성능을 개선하지만, LazyVStack만큼 효율적이지는 않습니다.

swift
struct ListView: View {
    let items = Array(1...1000).map { "Item \($0)" }

    var body: some View {
        List(items, id: \.self) { item in
            Text(item)
        }
    }
}

2. 사용 사례

List 사용 사례

List는 간단한 데이터 리스트와 정형화된 인터랙션이 필요한 경우에 적합합니다. 예를 들어, 연락처 목록, 할 일 목록, 설정 목록 등을 구현할 때 유용합니다.

swift
struct ContactListView: View {
    let contacts = ["John Doe", "Jane Smith", "Paul Brown"]

    var body: some View {
        List(contacts, id: \.self) { contact in
            Text(contact)
        }
    }
}

ScrollView + LazyVStack 사용 사례

ScrollView + LazyVStack은 복잡한 커스텀 레이아웃이 필요한 경우에 적합합니다. 예를 들어, 소셜 미디어 피드, 사진 갤러리, 상품 목록 등 다양한 레이아웃을 요구하는 경우 유용합니다.

swift
struct PhotoGalleryView: View {
    let photos = Array(1...100).map { "Photo \($0)" }

    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(photos, id: \.self) { photo in
                    Image(photo)
                        .resizable()
                        .scaledToFit()
                }
            }
        }
    }
}

3. 코드 예제 비교

동일한 데이터를 List와 ScrollView + LazyVStack으로 구현

아래는 동일한 데이터를 List와 ScrollView + LazyVStack으로 구현한 예제입니다. 이를 통해 두 방법의 차이점을 명확히 이해할 수 있습니다.

List 예제

swift
struct ItemsListView: View {
    let items = Array(1...20).map { "Item \($0)" }

    var body: some View {
        List(items, id: \.self) { item in
            Text(item)
        }
    }
}

ScrollView + LazyVStack 예제

swift
struct ItemsScrollView: View {
    let items = Array(1...20).map { "Item \($0)" }

    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(items, id: \.self) { item in
                    Text(item)
                }
            }
        }
    }
}

4. 고급 사용법

비동기 데이터 로딩

비동기 데이터 로딩은 네트워크 요청이나 큰 데이터 세트를 처리할 때 매우 유용합니다. Combine 프레임워크와 함께 사용하여 비동기 데이터를 로드하고, 이를 리스트에 반영할 수 있습니다.

swift
class ViewModel: ObservableObject {
    @Published var items: [String] = []

    func fetchData() {
        let url = URL(string: "https://api.example.com/items")!
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data {
                let items = try? JSONDecoder().decode([String].self, from: data)
                DispatchQueue.main.async {
                    self.items = items ?? []
                }
            }
        }.resume()
    }
}

struct AsyncListView: View {
    @StateObject private var viewModel = ViewModel()

    var body: some View {
        List(viewModel.items, id: \.self) { item in
            Text(item)
        }
        .onAppear {
            viewModel.fetchData()
        }
    }
}

필터링된 리스트 구현

사용자 입력에 따라 리스트 아이템을 필터링하는 기능을 구현할 수 있습니다. SearchBar를 사용하여 리스트를 동적으로 필터링하는 방법은 다음과 같습니다:

swift
struct FilteredListView: View {
    @State private var searchText = ""
    let items = ["Apple", "Banana", "Cherry", "Date", "Fig", "Grape"]

    var filteredItems: [String] {
        if searchText.isEmpty {
            return items
        } else {
            return items.filter { $0.contains(searchText) }
        }
    }

    var body: some View {
        VStack {
            SearchBar(text: $searchText)
            List(filteredItems, id: \.self) { item in
                Text(item)
            }
        }
    }
}

struct SearchBar: UIViewRepresentable {
    @Binding var text: String

    class Coordinator: NSObject, UISearchBarDelegate {
        @Binding var text: String

        init(text: Binding<String>) {
            _text = text
        }

        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            text = searchText
        }
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(text: $text)
    }

    func makeUIView(context: Context) -> UISearchBar {
        let searchBar = UISearchBar(frame: .zero)
        searchBar.delegate = context.coordinator
        return searchBar
    }

    func updateUIView(_ uiView: UISearchBar, context: Context) {
        uiView.text = text
    }
}

리스트 내비게이션

리스트 아이템을 선택하면 상세 화면으로 이동하는 네비게이션을 구현할 수 있습니다. NavigationView와 NavigationLink를 사용하여 다음과 같이 할 수 있습니다:

swift
struct NavigationListView: View {
    let items = ["Item 1", "Item 2", "Item 3"]

    var body: some View {
        NavigationView {
            List(items, id: \.self) { item in
                NavigationLink(destination: DetailView(item: item)) {
                    Text(item)
                }
            }
            .navigationTitle("Items")
        }
    }
}

struct DetailView: View {
    let item: String

    var body: some View {
        Text("Detail for \(item)")
            .navigationTitle(item)
    }
}

결론

SwiftUI의 List와 ScrollView + LazyVStack은 각각 고유의 장단점을 가지고 있으며, 특정 상황에서 더 적합한 방법을 선택할 수 있습니다. 성능 최적화와 커스텀 레이아웃이 필요할 때는 ScrollView + LazyVStack이 유리하며, 간단한 리스트와 기본적인 인터랙션이 필요할 때는 List가 유리합니다. 이 글을 통해 두 방법의 차이점을 명확히 이해하고, 자신의 필요에 맞는 적절한 방법을 선택할 수 있기를 바랍니다.

추가 자료

자세한 예제와 고급 사용법은 아래 자료를 참조하세요: