iOS

[Swift] Swift에서 Equatable을 이해하고 적용하기

GODOLs 2023. 10. 26. 15:24

오늘은 Swift 개발자로서 반드시 알아야 할 기본적인 프로토콜 중 하나인 'Equatable'에 대해 이야기해보려고 합니다. Equatable 프로토콜은 객체 비교를 간단하게 만들어 주며, 특히 SwiftUI와 같은 최신 Swift 기술에서 중요한 역할을 합니다.

1.Equatable 이란?

Equatable은 Swift 표준 라이브러리의 프로토콜 중 하나로, 사용자 정의 데이터 타입(클래스, 구조체 등)의 인스턴스 간에 동등 비교(==)를 가능하게 합니다. 기본 데이터 타입들은 이미 Equatable을 준수하고 있기 때문에, 우리는 문자열이나 정수 등을 쉽게 비교할 수 있습니다.

let a = 5
let b = 5
print(a == b) // true

하지만, 사용자 정의 데이터 타입의 경우, 어떤 조건에서 두 인스턴스가 "같다"고 할지 스스로 정의해야 합니다.

2. Equatable을 쓰는 이유

Equatable 프로토콜을 사용하는 주된 이유는 인스턴스의 동등성을 비교하여, 예상치 못한 버그를 방지하고, 데이터 정합성을 유지하며, 컬렉션에서의 객체 탐색이나 필터링 등을 용이하게 하기 위함입니다.

예를 들어, 사용자 정의 'Person' 구조체가 있을 때:

struct Person {
    var name: String
    var age: Int
}

let person1 = Person(name: "John", age: 25)
let person2 = Person(name: "John", age: 25)

print(person1 == person2) // 컴파일 오류

위 코드는 'Person'이 Equatable을 준수하지 않기 때문에 '==' 연산자를 사용할 수 없습니다. 따라서, 우리는 'Person'이 Equatable을 따르도록 해야 합니다:

extension Person: Equatable {
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
}

// 이제 비교가 가능합니다.
print(person1 == person2) // true

3. SwiftUI에서도 사용하는 경우

SwiftUI는 선언적 UI 구조를 사용하며, 데이터 상태의 변화에 따라 UI를 업데이트합니다. 여기서 Equatable의 사용은 UI의 성능 최적화와 밀접한 관련이 있습니다.

예를 들어, 리스트 뷰의 아이템이 Equatable을 준수할 경우, SwiftUI는 상태 변경 시 전체 리스트를 다시 그리지 않고 변경된 특정 아이템만 업데이트할 수 있습니다. 이는 특히 큰 리스트에서 성능 향상의 이점을 가져옵니다.

SwiftUI에서 Equatable의 중요성을 더 잘 보여주는 구체적인 예시를 들어 설명드리겠습니다. 여기서는 리스트의 아이템 삭제 시 Equatable의 역할을 중점적으로 살펴보겠습니다.

SwiftUI에서 Equatable 활용하기: 리스트에서 아이템 삭제

리스트 뷰에서 특정 아이템을 삭제하는 작업은 흔한 경우입니다. Equatable 프로토콜이 없다면, SwiftUI는 어떤 아이템이 삭제되었는지 정확히 알지 못하고 전체 뷰를 재구성할 수 있습니다. 이로 인해 성능 저하가 발생할 수 있습니다.

아래 예시에서는 사용자 정의 타입 Task가 Equatable을 준수하도록 구현하고, 이를 이용해 리스트에서 선택한 태스크를 삭제하는 UI를 만듭니다.

먼저, Task 모델을 정의합니다.

import SwiftUI

struct Task: Equatable, Identifiable {
    let id: Int
    var name: String
}

class TaskViewModel: ObservableObject {
    @Published var tasks: [Task] = [
        Task(id: 1, name: "Do laundry"),
        Task(id: 2, name: "Write blog post"),
        // more tasks...
    ]

    func removeTask(_ taskToRemove: Task) {
        tasks.removeAll { task in
            task == taskToRemove
        }
    }
}

여기서 Task 구조체는 Equatable과 Identifiable을 준수합니다.

이제 이 태스크 목록을 표시하고, 사용자가 특정 태스크를 삭제할 수 있는 SwiftUI 뷰를 만들어 보겠습니다.

struct TaskListView: View {
    @StateObject private var viewModel = TaskViewModel()

    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.tasks) { task in
                    Text(task.name)
                }
                .onDelete(perform: deleteTask)
            }
            .navigationBarTitle("Tasks")
        }
    }

    private func deleteTask(at offsets: IndexSet) {
        offsets.map { viewModel.tasks[$0] }.forEach(viewModel.removeTask)
    }
}

onDelete 메서드를 통해 사용자는 스와이프 제스처로 태스크를 삭제할 수 있습니다.

Equatable을 준수함으로써, 우리의 removeTask 함수는 실제로 삭제하려는 태스크가 무엇인지 정확히 알고, 그에 따라 적절한 액션을 취할 수 있습니다.

따라서 SwiftUI는 변경된 사항만을 반영하여 리스트 뷰를 업데이트할 수 있습니다.

이 예제를 통해 Equatable이 데이터의 동등성을 비교하는 데 얼마나 중요한지, 그리고 이를 통해 사용자 인터페이스의 성능을 어떻게 최적화할 수 있는지 보여줍니다.

특히 큰 목록이나 자주 업데이트가 필요한 UI에서 Equatable의 사용은 매우 중요합니다.

 


 

Equatable 프로토콜은 Swift에서 데이터의 동등성 비교에 필수적입니다.


특히 사용자 정의 데이터 타입을 작업할 때 이 프로토콜의 구현은 중요한데, 이를 통해 컬렉션 내 객체의 탐색, 정렬, 필터링 등을 수월하게 할 수 있습니다.

 

또한, SwiftUI 같은 프레임워크에서 UI 업데이트의 효율성을 크게 향상시키는 데에도 중요한 역할을 합니다.

Equatable은 코드의 명확성, 데이터 일관성, 그리고 성능 최적화를 위한 필수적인 도구입니다.


Swift에서 이 프로토콜을 활용하여 더 효율적이고 안정적인 애플리케이션을 개발할 수 있습니다.

반응형