2025. 4. 28. 20:15ㆍSwift, iOS Foundation
Equatable | Apple Developer Documentation
A type that can be compared for value equality.
developer.apple.com
Equatable을 언제 사용했더라?
아주 예전에 작성했던 글을 가지고 왔습니다.
[Swift] 제네릭 (Generic) 완전 정복하기라는 글에서 제네릭 타입에 특정 조건을 걸기 위한 기능, 제네릭 타입 제약 (Generic Type Constraints)을 설명하며
예시로 타입이 Equatable 프로토콜을 준수하도록 제약을 거는 코드를 소개한 적이 있었는데요.
이 글에서는 Equatable이 "타입끼리 비교 연산을 하기 위해 = 즉, 비교 연산자를 사용하기 위해 필수적으로 채택해야 하는 프로토콜이다"까지만 소개하고 넘어갔었습니다.
여기서의 비교 연산자 (Comparison Operators)를 명확하게 정의하고 넘어가자면,
Equatable과 연관된 연산자는 같다 (==) / 다르다 (!=)에 대한 부분만 다루고 있습니다.
Equate는 "같게 하다" "동등하게 생각하다/다루다" "동일시하다"와 같은 단어로 번역된다는 점에서
가능성을 뜻하는 부분 "able"을 더해 Equatable은 결국 직역했을 때 "같게 할 수 있는" "동일시할 수 있는" 정도로 읽을 수 있겠군요!
추가로 설명하면, 크다, 크거나 같다, 작다, 작거나 같다와 같은 세부 비교는 Comparable 프로토콜과 한 단계 더 연관되어 있습니다.
*그리고 Comparable은 기본적으로 "같음과 다름"에 대한 판단을 할 수 있어야하기 때문에 -> 내부적으로 Equatable을 채택하고 있습니다.
Equatable 내부 코드 뜯어보기
💡 A type that can be compared for value equality = 값의 동등성을 비교할 수 있는 타입
냅다 Equatable 프로토콜이 코드로는 어떻게 정의되어있는지. 해당 부분을 살펴보겠습니다.
우선 코드는 public protocol Equatable 부분과 extension Equatable 부분으로 나누어서 볼 수 있을 것 같군요.
public protocol Equatable에는 기본적인 프로토콜과,
해당 프로토콜을 채택했을 때 반드시 구현해야하는 정적 메서드 == 부분이 명시되어 있습니다.
- == 메서드는 타입 레벨에서 정의되어야 하는 비교 연산자이기 때문에 static을 붙였다. (a == b는 a 인스턴스의 메서드가 아니라, == 연산자가 호출하는 타입 메서드다.)
- 비교 대상은 두개가 모두 같은 타입이어야 하기 때문에 lfs와 rhs도 모두 Self다. (Int와 Int, String과 String을 비교하는 것은 가능하지만, Int와 String을 비교하는 것은 불가능하다는 것을 의미한다고 볼 수 있죠.)
extension Equatable은 기본적으로 구현 (default implementation)되어 있다고 명시되어 있습니다.
즉, Equatable을 채택하는 모든 타입에 대해 != 타입 메서드를 기본적으로 제공한다는 의미죠.
그러니 저희들은 !=에 대한 구현없이 == 부분만 직접 정의해주면 될 겁니다!
아마도 해당 익스텐션에 정의되어있는 메서드 구현부는 아래와 같이 정의되어 있을 거고요!
public static func != (lhs: Self, rhs: Self) -> Bool {
!(lhs == rhs)
}
그래서 Equatable이 뭔데?
코드를 뜯어보고 왔는데, 그래서 Equatable이 무엇인지 감이 오시는지요?
네. 쉽게 말하면 같다. 다르다를 판단할 수 있게 만들어주는 녀석입니다.
String, Int 등등... 기본 타입들은 같다 다르다를 우리가 자유롭게 비교할 수 있었잖아요? 내부적으로는 Equatable 프로토콜을 모두 채택하고 있었기 때문입니다.
그렇다면, 우리가 만든 커스텀 타입들...
Class나 Struct나 Enum 인스턴스에 대해서 같다/다르다를 판단하려고 한다면...?
아래와 같이 Binary operator '==' cannot be applied to two 'Account' operands라는 에러가 발생하고 있었는데요.
이때 필요한 것이 바로 Equatable입니다.
Account 구조체가 Equatable을 채택한다고 붙여주기만 했더니, 위의 에러가 사라지는 👀마법이랄까👀
구조체 (Struct)나 열거형 (Enum)의 경우에는 별도의 == 타입 메서드 정의 없이,
모든 프로퍼티가 Equatable을 채택하고 있는 타입인 경우라면 -> Equatable만 붙여주는 것만으로도 비교가 가능하게 됩니다.
하지만 클래스 (Class)의 경우는 조금 다릅니다.
아까 위에서 봤던 Equatable 프로토콜 정의부에 있던 메서드 == 부분까지 꼭 필수로 구현해야만 하죠.
!= 부분은 구조체와 마찬가지로 구현하지 않아도 되긴 합니다.
💬 클래스가 Equatable을 채택했을 때 == 메서드를 별도로 구현해야 하는 이유에 대한 생각
-
값 타입 (Value Type)인 구조체와 열거형과는 다르게 클래스는 참조 타입 (Reference Type)이죠.
즉, 구조체와 열거형은 "프로퍼티 값이 동일한지?"만 단순하게 비교하면 되지만
클래스는 이 두 개가 "같은 값인지?" "같은 메모리 주소를 갖는 같은 객체인지?"를 구분해줄 필요가 있었던 것입니다!
*참고로, 값 비교는 == 연산자로, 객체 비교 (= 메모리 주소 비교)는 === 연산자로 구분해서 할 수 있었습니다.
여기까지 글을 이만 정리해 볼까 합니다!
- Equatable은 값이 동일한지? 값이 다른지?를 비교하기 위해 채택할 수 있는 프로토콜이다.
- String이나 Int는 우리는 몰랐지만, 내부적으로 Equatable을 채택하고 있었기에 우리가 비교 연산자 (==, !=)를 사용하고 있었던 것.
- 구조체 (Struct)와 열거형 (Enum)은 프로퍼티 타입이 Equatable을 따른다면, 별도의 메서드 정의없이 Equatable을 붙어주는 것만으로도 사용할 수 있지만 / 클래스 (Class)는 조금 다르더라.