[Swift] 14. 프로토콜

Hoojeong Kim·2022년 3월 10일
0

Swift Base

목록 보기
16/22
post-thumbnail

프로토콜(Protocol)

프로토콜은 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다. 구조체, 클래스, 열거형은 프로토콜을 채택해서 특정 기능을 실행하기 위한 프로토콜의 요구사항을 구현할 수 있다. 즉, 프로토콜은 정의와 제시만을 할 뿐이지 스스로 기능을 구현하지는 않는다.

프로토콜은 왜 사용하나요?

잠깐 클래스의 상속을 떠올려보자. 우리는 클래스를 사용할 때 중복될만한 기능을 부모클래스에 정의해두고, 여러 자식클래스에서 상속받아 사용할 수 있다.

예를 들어, 클래스A에는 메서드A가, 클래스B에는 메서드B가 정의되어 있다. 또 다른 클래스C,D,E는 모두 메서드A를 필요로하고, 클래스C,D는 메서드B도 필요로한다.

클래스C,D,E는 메서드A를 사용하기 위해서 클래스A를 부모클래스로 삼아 상속받을 것이다.
하지만 클래스C,D는 메서드B도 필요하다. 이미 클래스A로부터 상속을 받았기 때문에 메서드B는 재사용성을 고려하지 않고 각각 구현해야 할까?

이때 사용할 수 있는 것이 바로 프로토콜이다. 프로토콜은 특정 역할을 하기위한 여러 기능들을 정의하며, 모든 클래스가 프로토콜을 채택할 수 있다. 이러한 점은 코드의 재사용성과 확장성을 지향한다.

이 밖에도 프로토콜은 다양하게 사용될 수 있으며, 개발에 꼭꼭! 필요하니 잘 알아두자.

프로토콜 정의

프로토콜은 구조체나 클래스, 열거형의 모양과 비슷하게 정의할 수 있으며 protocol 키워드를 사용한다.

protocol 프로토콜 이름 {
	프로토콜 정의
}

프로토콜 채택

구조체, 클래스, 열거형 등에서 프로토콜을 채택하려면, 타입 이름 뒤에 콜론(:)을 붙여준 후 채택할 프로토콜 이름을 쉼표(,)로 구분하여 명시한다.

protocol SomeProtocol {

}

protocol SomeProtocol2 {

}
struct SomeStruct: SomeProtocol, SomeProtocol2 {

}

class SomeClass: SomeProtocol, SomeProtocol2 {

}

enum SomeEnum: SomeProtocol, SomeProtocol2 {

}

이때 클래스가 상속을 받고 있다면, 부모클래스 뒤에 쉼표(,)로 구분하여 명시한다.
class ChildClass: ParentClass, SomeProtocol, SomeProtocol2 {

}

프로토콜 요구사항

프로토콜은 타입이 특정 기능을 실행하기 위해 필요한 기능을 요구한다. 프로토콜이 자신을 채택한 타입에 요구하는 사항은 프로터티나 메서드와 같은 기능들이다.

프로퍼티 요구

프로토콜은 자신을 채택한 타입이 어떤 프로퍼티를 구현해야 하는지 요구할 수 있다. 이때 프로토콜은 해당 프로퍼티의 종류(저장/연산 프로퍼티)는 신경쓰지 않지만, 읽기 전용인지 읽기/쓰기 전용인지는 get과 set을 사용해 명시해야 한다.

프로토콜을 채택한 타입은 프로토콜이 요구하는 프로퍼티의 이름과 타입만 맞도록 구현하면 된다.


만약 프로토콜이 읽기/쓰기 전용 프로퍼티를 요구한다면, 읽기만 가능한 상수 저장 프로퍼티와 읽기 전용 연산 프로퍼티를 구현할 수 없다. 반대로 프로토콜이 읽기 전용 프로퍼티를 요구한다면, 모든 프로퍼티를 구현할 수 있다.
프로토콜의 프로퍼티 요구는 항상 ``var`` 키워드를 사용한 변수 프로퍼티로 정의해야 한다. 그리고 읽기와 쓰기에 따라 get, set으로 나타낸다.
protocol FirstProtocol {
	var name: String { get set }
    var age: Int { get }
}

읽기/쓰기 전용일 때는 name과 같이, 읽기 전용일 때는 age와 같이 표현한다.


타입 프로퍼티를 요구하기 위해서는 ``static`` 키워드를 사용한다.
protocol AnotherProtocol {
	static var someTypeProperty: Int { get set }
}

그렇다면, 이제 정의한 프로토콜을 구조체에서 채택해보자.
struct SomeStruct: FirstProtocol {
	var name: String
    var age: Int
}

이때 프로토콜을 준수하기 위해서는 프로토콜에 정의한 프로퍼티나 메서드와 같은 기능을 모두! 사용해야 한다. 그렇지 않으면 오류 발생함!

메서드 요구

프로토콜은 특정 인스턴스 메서드나 타입 메서드를 요구할 수도 있다. 이때 메서드의 중괄호({}) 부분을 제외하고 메서드의 이름, 매개변수, 반환 타입 등만 작성한다. (가변 매개변수도 껴줌!)
추가로, 매개변수 기본 값은 지정할 수 없다.

protocol FirstProtocol {
	var name: String { get set }
    var age: Int { get }
    
    func someTypeMethod()
}

그렇다면, 이제 정의한 프로토콜을 구조체에서 채택해보자.
struct SomeStruct: FirstProtocol {
	var name: String
    var age: Int
    
    func someTypeMethod() {
    	print(name)
    }
}

이니셜라이저 요구

프로토콜은 프로퍼티, 메서드 등과 마찬가지로 특정한 이니셜라이저를 요구할 수도 있다. 프로토콜에서 이니셜라이저를 요구할 때, 메서드 요구와 마찬가지로 이니셜라이저를 정의하지만 구현을 하지 않는다.

protocol FirstProtocol {
	var name: String { get set }
    var age: Int { get }
    
    func someTypeMethod()
    
    init()
}

그렇다면, 이제 정의한 프로토콜을 구조체에서 채택해보자.
struct SomeStruct: FirstProtocol {
	var name: String
    var age: Int
    
    func someTypeMethod() {
    	print(name)
    }
    
    init() {
    
    }
}

이때 만약 클래스라면, 다음과 같이 ``required`` 식별자를 사용해야 한다.
class SomeClass: FirstProtocol {
	var name: String
    var age: Int
    
    func someTypeMethod() {
    	print(name)
    }
    
    required init() {
    
    }
}

하지만, 만약 클래스가 상속받을 수 없는 final 클래스라면 required 식별자를 붙여줄 필요가 없다.

profile
나 애기 개발자 👶🏻

0개의 댓글