Swift 입문 | 프로토콜 및 확장 | 프로토콜(Protocol)

Swift는 클래스에 포함된 메소드를 호출해 처리한다. 어떤 오브젝트를 처리할 때 “그 안에 어떤 메소드가 준비되어 있는가"를 생각하게 된다.

여기에 완전히 다른 클래스 객체가 여러개가 있었다고 하자. 그것들 모와서 어떠한 처리할 때 “모두에 공통된 메소드"를 제공하게 되면 매우 도움이 될 것이다.

그렇지만, 클래스가 다른 경우 각각에 같은 메소드가 준비되어 있을 지에 대해 전혀 알 수가 없다. 각각의 클래스가 상속 관계에 있으면, 슈퍼 클래스에 메소드를 준비해 두는 것으로 어떻게든 되겠지만, 상속 관계가 없는 클래스에 대해서는 같은 메소드가 있다는 것을 보증할 수 없다. “프로그래머가 신경을 써서 공통의 것을 준비해 둔다"가 아니라, “반드시 이 메소드가 준비되어 있다"고 확신할 수 있는가 라는 것이다.

이것을 가능하게 하는 것이 “프로토콜"이다. 프로토콜은 속성과 메소드 선언만 작성한 클래스와 같은 모양을 하고 있다. 작성 방법을 정리하면 이렇게 될 것이다.

protocol 프로토콜 이름 {
    ...... 속성과 메소드의 선언 ......
}

메소드는 {} 안에 구현 부분은 필요 없다. 단순히 “func ○○ (✕✕) -> △△;“라고 선언 부분만 있으면 된다.

이렇게 작성된 프로토콜은 클래스에 통합 이용된다. 이것은 클래스 상속과 기본적으로 유사하게 작성을 한다.

class 클래스명 : 프로토콜명 {...}

클래스의 상속을 하고 있는 경우는 “클래스명 : 슈퍼 클래스 이름, 프로토콜 이름 ……“와 같이, 상속 클래스명 뒤에 쉼표로 구분하여 작성한다. 또한 프로토콜은 다중를 설정할 수 있기에 이런 경우에도 각각을 쉼표로 구분하여 작성해 주면 된다.

이렇게 프로토콜을 설정된 클래스는 프로토콜에 포함된 모든 메소드를 구현해야 한다. 구현하지 않으면 오류가 발생하여 실행할 수 없다. 여러 클래스에 프로토콜을 설정하면, 그 클래스에 반드시 프로토콜의 메소드가 구현되어야 한다는 것이다.

 

그러면 실제로 프로토콜을 사용한 간단한 예제를 만들어 보자.

protocol MyProtocol {
    func printData ()
}

class PersonData: MyProtocol {
    var name:String = ""
    
    init(name:String) {
        self.name = name
    }
    
    func printData() {
        print("[name: \(name).]")
    }
}

class MemoData: MyProtocol {
    var content:String = ""
    
    init(content:String) {
        self.content = content
    }
    
    func printData() {
        print(content)
    }
}

var data:[MyProtocol] = []
data.append(PersonData(name: "kimkc"))
data.append(MemoData(content: "아침 9시부터 회의"))
for obj in data {
    obj.printData()
}

여기에서는 MyProtocol라는 프로토콜을 정의하고 거기에 데이터를 출력하는 printData라는 메소드가 작성되어 있다. 그리고 이 프로토콜을 구현한 클래스로 “PersonData “과 “MemoData"를 작성했다. PersonData는 개인 정보를 관리하는 것이고, MemoData 메모를 보관하는 것이다. 어느 쪽도 전혀 상속 관계도 없고, 사용할 수 있는 속성도 다르다.

클래스 정의 후에 이를 이용한 간단한 처리가 있다. PersonData과 MemoData를 배열로 모와서 반복적으로 내용을 출력한다.

여기에서는 MyProtocol의 배열을 만들고, 거기에 PersonData과 MemoData을 보관하고 있다. 프로토콜은 클래스과 같이 유형(타입)으로 지정할 수 있다.

MyProtocol 배열에는 MyProtocol를 구현한 클래스의 인스턴스라면 어떤 것이라도 넣을 수 있다. 그리고 MyProtocol에 캐스팅된 인스턴스에서 for문으로 printData를 호출하고 있다.

이 처럼, ‘프로토콜의 인스턴스로 캐스팅하고 메서드를 호출’이라는 방식으로 PersonData과 MemoData는 상속 관계도 아무것도 전혀 무관한 클래스를 한꺼번에 처리 할 수 있게 되는 것이다.