본문 바로가기

Project/MovieClip

✅ hash(into:) 메서드와 == 연산자의 역할

📍 구조체 내 hash, == 메서드

struct TvResultItem: Hashable {
    let tvResult: TvTMDBResult
    let sectionType: TvSectionType

    func hash(into hasher: inout Hasher) {
        // tvResult.id와 sectionType을 조합해서 고유 해시 생성
        hasher.combine(tvResult.id)
        hasher.combine(sectionType)
    }
    
    static func == (lhs: TvResultItem, rhs: TvResultItem) -> Bool {
        return lhs.tvResult.id == rhs.tvResult.id &&
               lhs.sectionType == rhs.sectionType
    }
}



✅ hash(into:) 메서드와 == 연산자의 역할

hash(into:)와 == 메서드는 Swift의 Hashable 및 Equatable 프로토콜을 구현할 때 필수적으로 고려해야 하는 요소입니다.

 

 

1️⃣ hash(into:) 메서드의 역할

hash(into:)는 객체의 해시값(Hash Value)을 생성하는 메서드입니다.

📌 역할

  • hash(into:)는 Set, Dictionary, 그리고 DiffableDataSource와 같은 데이터 구조에서 객체를 빠르게 비교하고 구별할 때 사용됩니다.
  • 특정 객체를 고유하게 식별할 때 해시값을 활용하며, 같은 해시값을 가지는 객체는 같은 것으로 인식됩니다.
  • hash(into:) 메서드를 구현하면 어떤 기준으로 객체를 고유하게 식별할지 정의할 수 있습니다.

코드 예제 (TvResultItem에서 hash(into:) 역할)

func hash(into hasher: inout Hasher) {
    hasher.combine(tvResult.id)  // 기본적으로 같은 TV 프로그램은 같은 id를 가짐
    hasher.combine(sectionType)  // 하지만 다른 섹션이라면 다른 객체로 인식하게 함
}

 

💡 여기서 hasher.combine(sectionType)이 중요한 이유

  • 같은 id를 가진 TvTMDBResult라도 섹션이 다르면 다른 객체로 취급하도록 하기 위해서! 
    • 두 객체는 id: 1001로 동일하지만, sectionType이 다름.
    • hash(into:)를 올바르게 구현하면, 이 두 개가 다른 객체로 인식됨.
TvResultItem(tvResult: TvTMDBResult(id: 1001, name: "Breaking Bad"), sectionType: .airingToday)
TvResultItem(tvResult: TvTMDBResult(id: 1001, name: "Breaking Bad"), sectionType: .popular)
 

2️⃣ == 연산자 오버로딩 (Equatable)

📌 역할

  • == 연산자는 두 개의 객체가 같은지 비교할 때 사용됩니다.
  • 기본적으로 Hashable을 채택하면 Equatable도 자동으로 적용되지만, 기본 비교는 모든 속성이 동일한 경우에만 true를 반환합니다.
  • 우리가 원하는 것은 "id가 같더라도 섹션이 다르면 다른 객체로 인식하는 것"이므로 직접 == 연산자를 구현하는 것이 중요합니다.

코드 예제 (TvResultItem에서 == 역할)

static func == (lhs: TvResultItem, rhs: TvResultItem) -> Bool {
    return lhs.tvResult.id == rhs.tvResult.id &&
           lhs.sectionType == rhs.sectionType  // ✅ 같은 id라도 섹션이 다르면 다른 객체로 인식!
}

 

💡 왜 ==을 직접 구현해야 할까?

  • Swift는 기본적으로 모든 프로퍼티가 같아야 동일한 객체라고 판단합니다.
  • 하지만 우리는 tvResult.id와 sectionType만 비교하여, 같은 TV 프로그램이라도 다른 섹션이면 다른 객체로 취급되도록 해야 합니다.

 

3️⃣ hash(into:)와 ==이 없어도 동작하는 이유

💡 이 두 메서드가 없어도 정상적으로 동작하는 이유는?

  • Swift의 Hashable 프로토콜은 자동으로 모든 프로퍼티를 기반으로 해시값을 생성합니다.
  • 즉, hash(into:)를 직접 정의하지 않아도, Swift가 내부적으로 다음과 같이 처리합니다.  
    • Swift는 자동으로 tvResult 내부의 모든 프로퍼티를 해시값 계산에 사용합니다.
    • 또한, == 연산자 역시 모든 프로퍼티가 같아야 true를 반환하도록 기본 구현됨.
func hash(into hasher: inout Hasher) {
    hasher.combine(tvResult)
    hasher.combine(sectionType)
}

 

그럼에도 불구하고 우리가 hash(into:)와 ==을 직접 정의하는 이유는?

  1. 최적화: 자동 생성된 hash(into:)는 모든 프로퍼티를 해싱하지만, 우리는 필요한 프로퍼티(id와 sectionType)만 해싱하여 성능을 최적화할 수 있음.
  2. 명확성: 우리가 비교하려는 핵심 요소(id와 sectionType)를 명확하게 지정할 수 있음.
  3. 예측 가능한 동작 보장: Swift의 자동 구현 방식이 내부적으로 변경될 가능성을 줄이고, 의도한 대로 정확한 비교를 수행하도록 보장.

 

4️⃣ hash(into:)와 ==을 만드는 방법

📌 직접 만들 때 고려해야 할 점

  • 어떤 기준으로 객체를 고유하게 식별할지 정해야 함.
  • 같은 객체로 취급해야 할 요소만 hash(into:)와 == 연산자에 포함해야 함.

기본 예제 (학생 데이터)

struct Student: Hashable {
    let id: Int
    let name: String
    let grade: Int
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)  // ✅ id가 같으면 같은 학생으로 간주
    }

    static func == (lhs: Student, rhs: Student) -> Bool {
        return lhs.id == rhs.id  // ✅ id만 같으면 같은 학생으로 취급
    }
}

 

✅ 고급 예제 (섹션 포함)

struct MovieItem: Hashable {
    let movieID: Int
    let section: String  // ✅ 같은 영화라도 다른 섹션에 있을 수 있음

    func hash(into hasher: inout Hasher) {
        hasher.combine(movieID)
        hasher.combine(section)  // ✅ 같은 영화라도 다른 섹션이면 다른 객체로!
    }

    static func == (lhs: MovieItem, rhs: MovieItem) -> Bool {
        return lhs.movieID == rhs.movieID &&
               lhs.section == rhs.section
    }
}

 

 

🛠️ hash(into:)와 ==을 구현해야 하는 경우

✅ 같은 id를 가진 객체라도 추가적인 구분이 필요할 때
✅ 해싱 기준을 최적화하고 싶을 때
✅ 특정 프로퍼티만 동일한지 확인하고 싶을 때

🛠️ hash(into:)와 ==을 구현하지 않아도 되는 경우

✅ 기본적인 동작(모든 프로퍼티 비교)이 우리가 원하는 동작과 일치할 때
✅ 모든 프로퍼티가 동일할 때만 같은 객체로 인식하고 싶을 때


💡 결론:

  • hash(into:)와 == 연산자를 정의하면 같은 id라도 sectionType이 다르면 다른 객체로 인식할 수 있음 
  • 따라서 Diffable Data Source에서 중복 문제 없이 같은 TV 프로그램을 여러 섹션에서 보여줄 수 있음 
  • 기본적으로 없어도 동작은 하지만, 명확한 비교 기준을 직접 설정하고 성능을 최적화하려면 구현하는 것이 좋음