타입으로써의 프로토콜(Protocols as Type)
- Swift에서 프로토콜은 타입(형식)으로 사용할 수 있다
- 이는 프로토콜이 일급 객체(First Class Citizen) 이기 때문이다
- 즉, 프로토콜은 클래스, 구조체, 열거형과 동일하게 타입으로 취급될 수 있다
일급 객체(First Class Citizen)
- 프로그래밍 언엉에서 일급 객체란 다음과 같은 조건을 모두 만족하는 객체를 말한다
- 변수나 네이터 구조에 담을 수 있다
- 함수의 인자(파라미터)로 전달할 수 있다
- 함수의 반환값으로 사용할 수 있다
- 할당된 객체는 언제든지 생성, 수정, 삭제가 가능하다
- Swift에서 프로토콜은 일급 객체이므로, 이러한 모든 특성을 만족한다
- 예를 들어, 프로토콜 타입을 변수로 선언하거나 함수의 파라미터로 전달하는 것이 가능하다
프로토콜 선언 및 구현
Appliance라는 프로토콜을 선언하고, 두 가지 타입(WashingMachine 클래스, AirConditioner 구조체)에 구현하는 예제
protocol Appliance {
func start()
func stop()
}
class WashingMachine: Appliance {
func start() {
print("세탁기 시작")
}
func stop() {
print("세탁기 정지")
}
}
struct AirConditioner: Appliance {
func start() {
print("에어컨 켜기")
}
func stop() {
print("에어컨 끄기")
}
func setTemperature(to temperature: Int) {
print("\(temperature)도로 설정")
}
}
프로토콜 타입 사용의 장점
프로토콜 타입 배열 사용하기
- 여러 객체를 동일한 프로토콜 타입으로 묶어서 관리할 수 있다
let devices: [Appliance] = [WashingMachine(), AirConditioner()]
for device in devices {
device.start()
}
- 위의 코드에서
devices 배열은 Appliance 타입으로 선언되었기 때문에, 배열에 담긴 객체들은 모두 Appliance 프로토콜의 요구 사항(start(), stop())만 사용 가능하다
프로토콜 타입으로 저장 시의 주의점
- 프로토콜 타입으로 저장하면 프로토콜에서 선언된 메서드나 속성만 접근 가능하다
- 즉, 객체가 실제로 추가 구현한 메서드나 속성에는 접근할 수 없다
- 프로토콜 타입으로 저장할 때는 그 객체가 어떤 타입인지 모르기 때문에, 프로토콜에 정의된 기능만 사용할 수 있다
let aircon: Appliance = AirConditioner()
aircon.start()
aircon.stop()
- 위의 예제에서
aircon은 Appliance 타입으로 저장되었기 때문에, 실제로는 AirConditioner 객체일지라도 Appliance 프로토콜에 선언된 메서드만 호출할 수 있다
- 만약
setTemperature() 메서드를 호출하려면 타입을 변환(다운캐스팅)해야 한다
프로토콜 준수성 검사 (Type Checking)
- Swift에서는
is와 as 연산자를 사용하여 객체가 특정 프로토콜을 준수하는지 확인하거나, 타입 변환을 수행할 수 있다
is 연산자 (타입 확인)
- 특정 인스턴스가 프로토콜을 준수하는지 확인할 수 있다
- 반대로, 프로토콜 타입이 더 구체적인 타입인지 확인할 수도 있다
let aircon = AirConditioner()
print(aircon is Appliance)
print(devices[1] is AirConditioner)
as 연산자 (타입 변환)
as 키워드는 업캐스팅(Upcasting) 과 다운캐스팅(Downcasting) 에 사용된다
업캐스팅 (as 연산자)
let device: Appliance = aircon as Appliance
device.start()
device.stop()
다운캐스팅 (as? / as! 연산자)
- 프로토콜 타입으로 저장된 인스턴스를 실제 타입으로 변환한다
as? : 옵셔널로 변환 (타입이 맞지 않으면 nil 반환)
as! : 강제 변환 (타입이 맞지 않으면 런타임 에러 발생)
if let airConditioner = devices[1] as? AirConditioner {
airConditioner.setTemperature(to: 20)
}
요약
- Swift에서 프로토콜은 일급 객체 이므로 변수나 배열에 저장할 수 있고, 함수의 인자로 전달되거나 반환값으로 사용될 수 있다
- 또한,
is 연산자를 사용하여 타입 확인이 가능하며, as 연산자를 사용하여 타입 변환을 안전하게 수행할 수 있다
- 중요하게도, 프로토콜 타입으로 저장할 경우에는 해당 프로토콜에 정의된 메서드나 속성만 호출할 수 있다
- 이를 통해 프로토콜을 사용하면 객체의 타입을 명확히 하지 않아도 공통된 기능을 쉽게 사용할 수 있게 해주며, 코드의 유연성과 확장성 을 크게 높일 수 있다