Swift에서 프로토콜(Protocol)은 특정 기능을 정의하고, 이를 여러 타입에서 구현하도록 하는 강력한 개념이다.
그 중에서도 Comparable
, Equatable
, Hashable
같은 기본 프로토콜들은 정렬, 비교, 딕셔너리 키 활용 등 다양한 기능을 제공한다.
Comparable 프로토콜
Comparable이란?
Comparable은 값을 비교할 수 있도록 해주는 프로토콜이다.
이를 준수하면 >(크다), <(작다), >=(크거나 같다), <=(작거나 같다) 등의 비교 연산자를 사용할 수 있다.
Comparable 프로토콜 구현하기
Person 구조체에서 Comparable 적용
static func <
를 구현하여 나이를 기준으로 비교 가능
struct Person: Comparable {
let name: String
let age: Int
static func < (lhs: Person, rhs: Person) -> Bool {
return lhs.age < rhs.age
}
}
Comparable을 활용한 정렬
sorted()
함수를 사용하면 자동으로 나이를 기준으로 정렬됨
정렬 기준을 커스텀하고 싶다면 sorted(by:)
를 활용
let sortedByName = people.sorted { $0.name < $1.name }
Equatable 프로토콜
두 개의 객체를 비교할 수 있도록 하는 프로토콜
==
연산자를 사용할 수 있게 해줌
기본 타입들은 Equatable을 자동으로 채택
let a = 5
let b = 5
print(a == b) // true (Int는 기본적으로 Equatable)
사용자 정의 타입에서 Equatable 구현
struct User: Equatable {
let id: Int
let name: String
}
Equatable을 준수하는 경우, Swift가 자동으로 ==
를 구현
let user1 = User(id: 1, name: "춘장")
let user2 = User(id: 2, name: "메주")
print(user1 == user2) // false
비교 연산이 필요한 경우 Equatable을 적용하는 것이 필수!
Hashable 프로토콜
객체를 해시 값으로 변환할 수 있도록 하는 프로토콜
Dictionary 또는 Set의 키로 사용할 수 있도록 함
Hashable을 구현하면 Dictionary의 키로 사용 가능
Dictionary에서 키로 활용할 때 Hashable을 적용하면 효율적인 검색이 가능
struct Product: Hashable {
let id: Int
let name: String
}
var productSet: Set<Product> = []
productSet.insert(Product(id: 1, name: "MacBook"))
productSet.insert(Product(id: 2, name: "iPhone"))
print(productSet.contains(Product(id: 1, name: "MacBook"))) // true
Custom Hash Function을 만들 수도 있음
struct CustomUser: Hashable {
let id: Int
let username: String
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(username)
}
}
Codable (Encodable & Decodable) 프로토콜
JSON과 Swift 객체 간 변화를 쉽게 해주는 프로토콜
API 통신 시 데이터를 직렬화(Serialize)하거나 역직렬화(Deserialize)할 때 사용
Codable을 활용한 JSON 디코딩 예제
Codable을 사용하면 JSON 데이터를 Swift 구조체로 쉽게 변환 가능
struct User: Codable {
let id: Int
let name: String
}
let jsonData = """
{
"id": 1,
"name": "감태"
}
""".data(using: .utf8)!
let user = try? JSONDecoder().decode(User.self, from: jsonData)
print(user?.name) // "감태"
Sequence & Collection 프로토콜
배열과 같은 컬렉션을 구현할 때 필요한 프로토콜
for-in
루프를 사용할 수 있도록 해줌
Sequence 프로토콜 구현
Sequence를 활용하면 사용자 정의 반복 구조를 만들 수 있음
struct Countdown: Sequence, IteratorProtocol {
var count: Int
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}
let countdown = Countdown(count: 5)
for number in countdown {
print(number) // 5, 4, 3, 2, 1 출력
}
CustomStringConvertible 프로토콜
객체를 문자열로 변환할 때 사용
print()
시 커스텀 문자열을 출력 가능
객체의 출력 형식을 사용자 정의할 수 있어 디버깅에 유용
struct Car: CustomStringConvertible {
let brand: String
let model: String
var description: String {
return "🚗 \(brand) \(model)"
}
}
let myCar = Car(brand: "Tesla", model: "Model S")
print(myCar) // 🚗 Tesla Model S