Swift에서 프로토콜은 타입 간의 공통된 인터페이스를 정의하기 위해 사용됩니다.
이러한 프로토콜은 일반 타입처럼 변수나 매개변수의 타입으로도 사용 가능한데, 이를 프로토콜 타입(protocol type)이라고 부릅니다.
하지만 모든 프로토콜을 일반 타입처럼 사용할 수 있는 것은 아니며, 특히 Self 요구나 associated type이 있는 경우에는 타입 지우기(Type Erasure)가 필요합니다.
이 글에서는 프로토콜 타입의 기본 개념과 함께, 타입 지우기의 개념과 구현 방법을 살펴보겠습니다.
1. 프로토콜 타입이란?
프로토콜을 타입처럼 사용할 수 있으며, 이를 통해 여러 타입을 하나로 묶을 수 있습니다.
protocol Drawable {
func draw()
}
struct Circle: Drawable {
func draw() {
print("원을 그립니다.")
}
}
struct Square: Drawable {
func draw() {
print("사각형을 그립니다.")
}
}
let shapes: [Drawable] = [Circle(), Square()]
shapes.forEach { $0.draw() }
shapes
는 서로 다른 타입(Circle, Square)이지만, Drawable
이라는 공통 프로토콜로 타입이 통일되어 있습니다.
2. associatedtype과 프로토콜 타입의 제약
associatedtype
을 포함한 프로토콜은 컴파일러가 구체 타입을 알 수 없기 때문에 프로토콜 타입으로 직접 사용할 수 없습니다.
protocol Container {
associatedtype Item
var items: [Item] { get }
}
이 프로토콜을 직접 타입으로 쓰면 오류가 발생합니다.
// 오류 발생: 'Container'는 'associatedtype'을 포함하고 있으므로 직접 사용할 수 없습니다.
func printContainer(_ c: Container) {
print(c.items)
}
이러한 경우 타입 지우기(type erasure)를 사용해야 합니다.
3. 타입 지우기(Type Erasure)란?
타입 지우기는 프로토콜의 구체 타입을 감추고, 외부에는 프로토콜 타입처럼 보이게 하는 기술입니다.
이를 통해 associatedtype
이 있는 프로토콜도 일반 타입처럼 사용할 수 있습니다.
4. 예제: 타입 지우기 구현
먼저 프로토콜을 정의합니다.
protocol Printable {
associatedtype Content
func printContent(_ content: Content)
}
그리고 이 프로토콜을 채택하는 여러 타입을 정의합니다.
struct StringPrinter: Printable {
func printContent(_ content: String) {
print("문자열 출력: \(content)")
}
}
struct IntPrinter: Printable {
func printContent(_ content: Int) {
print("정수 출력: \(content)")
}
}
Printable
은 associatedtype
이 있으므로 [Printable]
같은 배열을 만들 수 없습니다.
이를 해결하기 위해 타입 지우기를 구현합니다.
5. AnyPrintable: 타입 지우기 래퍼 구현
class AnyPrintable<T>: Printable {
private let _print: (T) -> Void
init<P: Printable>(_ printer: P) where P.Content == T {
_print = printer.printContent
}
func printContent(_ content: T) {
_print(content)
}
}
이제 다양한 프린터를 하나의 배열로 사용할 수 있습니다.
let printers: [AnyPrintable<String>] = [
AnyPrintable(StringPrinter())
// AnyPrintable(IntPrinter()) // 오류: 타입 불일치
]
printers.forEach { $0.printContent("Hello") }
타입이 지워졌기 때문에 외부에서는 Printable이라는 공통 인터페이스만 인식하고, 내부 구현은 알 수 없습니다.
마무리
Swift에서 프로토콜 타입은 다양한 객체를 통합적으로 다룰 수 있도록 해주는 강력한 기능입니다.
하지만 associatedtype
이나 Self
요구가 포함된 프로토콜은 제한이 있으므로, 이때 타입 지우기를 통해 유연하게 처리할 수 있습니다.
타입 지우기를 적절히 활용하면 제네릭과 프로토콜 기반 설계를 더 유연하게 확장할 수 있습니다.