Hashable에 대해 알아봅시다

som·2023년 12월 29일
0

SwiftAnatomy

목록 보기
6/9

2023년이 얼마 남지 않았네요....ㅠ
고로.. Hashable이 2023년의 마지막 포스트가 되겠네요 ㅎㅎ

개념

public protocol Hashable: Equatable {
    var hashValue: Int { get }

    func hash(into hasher: inout Hasher)
    func _rawHashValue(seed: Int) -> Int
}

해당 오픈 소스 코드는 swift > stdlib > public > core > Hashable.swift에서 자세히 볼 수 있습니다.

정수 해시 값을 생성하기 위해 Hasher로 해시될 수 있는 유형입니다. Set 또는 Dictionary KeyHashable 프로토콜을 준수하는 모든 타입을 사용할 수 있습니다. 표준 라이브러리의 많은 타입은 Hashable을 준수합니다. String, Int, FloatBool, 심지어 Set도 기본적으로 해시 가능합니다. Optional, Array, Ranges와 같은 일부 다른 유형은 해당 타입 인수가 동일하게 구현되면 자동으로 해시 가능해집니다.

사용자 정의 타입도 Hashable이 가능합니다. 연관된 값 없이 열거형을 정의하면 Hashable 적합성을 자동으로 얻고, hash(into:) 메서드를 구현하여 다른 사용자 정의 타입에 Hashable 적합성을 추가할 수 있습니다. 저장된 프로퍼티가 모두 Hashable인 구조체와 모든 Hashable 관련 값이 있는 열거형 타입의 경우 컴파일러는 자동으로 hash(into:) 구현을 제공할 수 있습니다.

값을 해싱하는 것은 Hasher 타입으로 표시되는 해시 함수에 필수 구성 요소를 공급하는 것을 의미합니다. 필수 구성 요소는 유형의 Equatable 구현에 기여하는 구성 요소입니다. 동일한 두 인스턴스는 hash(into:)Hasher에 동일한 값을 동일한 순서로 공급해야 합니다.

공식문서는 Hashable을 어떻게 사용하는지를 집중적으로 알려주고 있습니다.
그런데 hashValue, Hasher 이런 개념들은 해당 문서만 보고 이해하기에는 아리송합니다.

hashValue

hashValue는 어떤 데이터가 와도 64비트짜리 정수로 치환됩니다. 즉 암호화 작업을 한다고 생각하면 이해가 쉬울 거 같습니다. 원본 데이터를 특정 규칙에 따라 처리하여 간단한 숫자로 만든 것이고, 2개의 데이터 비교시 데이터가 동일할 때 둘의 해시값은 같습니다.

Hasher

Set 및 Dictionary에서 사용되는 범용 해시 함수입니다.

Hasher는 임의의 바이트 시퀀스를 정수 해시 값으로 매핑하는 데 사용할 수 있습니다. combine() 메서드를 변경하는 일련의 호출을 사용하여 해셔에 데이터를 공급할 수 있습니다. 해시 입력이 끝나면 finalize()를 호출하여 해시 값을 검색할 수 있습니다.

var hasher = Hasher()
hasher.combine(23)
hasher.combine("Hello")
let hashValue = hasher.finalize()

Swift 프로그램 실행 내에서 Hasher는 완전히 동일한 바이트 시퀀스가 공급되는 한 프로그램 종료가 항상 동일한 해시 값을 생성하도록 보장합니다. 그러나 기본 해시 알고리즘은 눈사태 효과를 나타내도록 설계되었습니다. 즉, 시드 또는 입력 바이트 시퀀스가 약간 변경되면 일반적으로 생성된 해시 값이 크게 변경됩니다.

또 모르는 개념이 나왔습니다! 눈사태 효과라... 느낌상 나비효과처럼 작은 변화가 전체적으로 변화를 주는 개념같이 생각이 됩니다.

눈사태 효과

눈사태 효과는 암호학 용어로, 원문(Plaintext)의 한 비트의 변화가 최종 암호문(Ciphertext)에 큰 변화를 주는 효과입니다.

설명이 너무 간단한 거 같지만.. 너무 깊게 파면 암호학까지 공부하게 될 거 같아서 여기까지만 알아보기로 했습니다...ㅎㅎ

그러면 조금 더 코드를 뜯어볼까요?

func hash(into hasher: inout Hasher)

이 값의 필수 구성 요소를 지정된 Hasher에 공급하여 해시합니다. Hashable 프로토콜을 준수하도록 이 메서드를 구현하세요. 해싱에 사용되는 구성 요소는 타입의 == 연산자 구현에서 비교되는 구성 요소와 동일해야 합니다. 이러한 각 구성 요소와 함께 hasher.combine(_:)을 호출하세요.

hash(into:) 구현 시 제공된 hasher 인스턴스에서 finalize()를 호출하지 않거나 다른 인스턴스로 바꾸지 마세요. 그렇게 하면 나중에 컴파일 시간 오류가 발생할 수 있습니다.

오~ 미리 Hasher에 대해 공부하길 잘한 거 같네요👍

Swift 표준 라이브러리에 속한 타입들은 기본적으로 Hashable을 채택하고 있어서 사용자 정의 타입에 Hashable을 채택해도, 저장 프로퍼티가 모두 표준 라이브러리에 속한 타입들이라 따로 이 메서드를 구현해본 적은 없는 거 같아요.

설명을 읽어보니, 해당 값을 Hasher로 암호화하는 기능을 하는 메서드로 보입니다.

func _rawHashValue(seed: Int) -> Int

원시 최상위 해싱 인터페이스. 일부 표준 라이브러리 타입(주로 기본 라이브러리)은 이를 전문화하여 작은 복원력 오버헤드를 제거합니다.

소스 코드 주석에 딱 이렇게만 쓰여있어서 으잉? 하고 따로 공식문서가 있는지 찾아보았으나... 이 메서드에 대한 공식 문서는 없더라고요. 그래서 이 부분은 몰루? 상태로 넘어가야될 거 같습니다.. (더 성장해서 오겠습니다😅)

간단정리

Hash 자체는 데이터의 암호화를 위해 사용되는 알고리즘입니다.
Swift에서는 DictionarySet의 특정 요소를 검색하기 위한 수단으로 많이 사용됩니다. Array와 다르게 순서라는 개념이 존재하지 않기 때문이죠.

오늘은 간단하게 Hashable에 대해 공부해보았습니다.
연말이다보니, 조금은 드문드문 포스팅하고 있지만... 다음주부터는 열심히 올리려고 합니다!

profile
얼레벌레 취준 공부 중인 초보 개발자

0개의 댓글