Swift에서 프로토콜은 특정 기능에 필요한 메서드, 프로퍼티 및 기타 요구사항의 청사진을 정의합니다. 프로토콜을 실제 타입에서 채택하고 구현할 때는 타입의 특성(참조 타입 또는 값 타입)에 따라 get, set 요구사항과 mutating 키워드의 사용법을 정확히 이해해야 합니다.
1. 프로퍼티 요구사항: get과 set
프로토콜에서 프로퍼티를 정의할 때는 해당 프로퍼티가 읽기 전용인지, 혹은 읽기와 쓰기가 모두 가능한지를 명시해야 합니다.
var identifier: String { get }
위와 같이 선언된 프로퍼티는 구현체에서 최소한 읽기 기능(get)을 제공해야 함을 의미합니다.
var identifier: String { get set }
반면, get set이 모두 명시된 경우 구현체는 반드시 읽기와 쓰기가 모두 가능한 변수 프로퍼티로 이를 구현해야 합니다.
2. 값 타입을 위한 mutating 키워드
클래스와 달리 구조체(Struct)나 열거형(Enum)은 값 타입(Value Type)입니다. 프로토콜의 메서드가 인스턴스 내부의 상태를 변경해야 한다면, 프로토콜 정의 시 메서드 앞에 mutating 키워드를 추가해야 합니다. 클래스에서 이를 구현할 때는 mutating을 생략할 수 있지만, 구조체나 열거형에서는 필수적으로 명시해야 합니다.
3. 프로토콜 정의 예시
상태를 나타내는 프로퍼티와 상태를 변경하는 메서드를 포함하는 프로토콜을 다음과 같이 정의할 수 있습니다.
protocol Toggleable {
var currentState: String { get }
mutating func toggle()
}
4. 클래스(Class)에서의 구현
클래스는 참조 타입이므로 메서드 내에서 프로퍼티를 수정할 때 별도의 키워드가 필요하지 않습니다.
class DeviceManager: Toggleable {
var currentState: String = "Off"
func toggle() {
if currentState == "Off" {
currentState = "On"
} else {
currentState = "Off"
}
}
}
let manager = DeviceManager()
manager.toggle()
print(manager.currentState) // "On" 출력
클래스 인스턴스를 let으로 선언하더라도 클래스 내부의 가변 프로퍼티는 수정이 가능합니다.
5. 구조체(Struct)에서의 구현
구조체에서 프로토콜의 수정 메서드를 구현할 때는 반드시 mutating을 붙여야 합니다.
struct LightSwitch: Toggleable {
var currentState: String = "Internal Off"
mutating func toggle() {
currentState = (currentState == "Internal Off") ? "Internal On" : "Internal Off"
}
}
var mySwitch = LightSwitch()
mySwitch.toggle()
print(mySwitch.currentState)
구조체는 값 타입이므로 인스턴스 상태를 변경하려면 해당 인스턴스가 var로 선언되어야 합니다. let으로 선언된 구조체 인스턴스에서는 toggle() 메서드를 호출할 수 없습니다.
6. 열거형(Enum)에서의 구현
Swift의 열거형은 연관값(Associated Values)을 가질 수 있으며, 프로토콜 요구사항을 충족하기 위해 계산된 프로퍼티와 mutating 메서드를 사용할 수 있습니다.
enum OperationState: Toggleable {
case active(String), inactive(String)
var currentState: String {
switch self {
case .active(let message):
return "Active: \(message)"
case .inactive(let message):
return "Inactive: \(message)"
}
}
mutating func toggle() {
switch self {
case .active(let msg):
self = .inactive(msg)
case .inactive(let msg):
self = .active(msg)
}
}
}
var state = OperationState.inactive("System standby")
print(state.currentState)
state.toggle()
print(state.currentState)
열거형에서의 mutating 메서드는 self 자체를 새로운 케이스로 교체하는 방식으로 상태 변경을 수행합니다. 프로토콜에서 정의된 get 요구사항은 열거형의 연관값을 활용한 계산된 프로퍼티로 깔끔하게 구현할 수 있습니다.