안녕하세요, 우디입니다.
이번 글에서는 Swift에서 사용 가능한 프로토콜에 대해 이야기해보려고 합니다.
기능에 대한 정확한 정보와 사용 방법은 언제나! 꼭! 공식문서를 참고하시길 바랍니다.
Protocol 이란?
프로토콜은 특정 작업이나 기능의 조각에 맞게 메서드, 프로퍼티 및 기타 요구 사항의 청사진을 정의합니다.
프로토콜의 요구 사항이 정의됐다면 실제 구현을 제공하기 위해 class, struct, enum 등에 의해 채택될 수 있습니다.
// 프로토콜 정의
protocol Calculatable {
func plus(number: Int)
}
// 프로토콜 채택
class Item: Calculatable {
let name: String
var quantity: Int
func plus(number: Int) {
self.quantity += number
}
}
(줄바꿈이 이상하네요... 해결 방법을 아시는 분은 댓글 남겨주시면 감사하겠습니다 😂)
이렇게 프로토콜을 정의하고, 프로토콜을 채택한 타입은 요구사항(func plus)이 있는 것들을 구현해야 합니다.
그럼 프로토콜은 왜 쓰나요?
프로토콜의 기본 개념은 간단합니다. 요구사항을 정의하고, 프로토콜을 채택한 타입에게 요구사항을 구현하도록 강제하는 것이죠.
그럼 프로토콜은 단순히 '요구'만 하는 기능일까요? 단순하게 요구사항만 나타내는 것이라면 기획팀에서 개발팀에게 요구하는 기능 명세만 있어도 충분하지 않을까요?
프로토콜의 공식문서를 더 읽다보면 프로토콜로 구현 가능한 다양한 기법들이 있습니다.
대표적으로 Delegation, Extension Protocol, Synthesized Implementation, Class-Only 등등의 아주 다양하고 유용한 기법들이 있으니 꼭 공식문서와 함께 공부해보시는걸 추천드립니다.
그럼 이 기법 중 몇 가지를 알아보면서 왜 프로토콜을 배워야하고 사용해야 하는지 알아보도록 하겠습니다.
Delegation
딜리게이션은 딜리게이트 패턴이라고도 하며 UIKit에서도 많이 사용하는 패턴입니다. ex) UITableView, UICollectionView
(Apple Developer - UITableView 프로퍼티 delegate 참고)
타입의 일부 책임(혹은 기능)을 다른 유형의 인스턴스에 넘길 수 있도록 구현하는 디자인 패턴입니다.
Extension Protocol
확장을 통한 프로토콜 구현은 상속을 받지 못하는 구조체와 열거형에게 상속과 유사한 형태로 기능을 확장하는 방법입니다.
프로토콜의 요구사항을 익스텐션으로 구현한 후 프로토콜을 채택하면 채택한 타입은 프로토콜의 구현된 요구사항을 타입 안에서 구현 없이 바로 사용할 수 있습니다.
Synthesized Implementation
스위프트는 자동으로 프로토콜의 적합성을 제공 할 수 있습니다! 적합성을 제공한다...? 가 다소 어려울 수 있는데요 대표적인 예시로 Equatable이라는 프로토콜은 스위프트에서 기본적으로 제공하는 프로토콜입니다. 이 프로토콜을 채택하면 요구 사항을 직접 구현하기 위해 코드를 작성할 필요가 없습니다.
Class-Only
클래스 전용 프로토콜은 딜리게이트 패턴을 구현할 때 사용할 수 있는데요. 딜리게이트 패턴에 사용하는 프로토콜로 인해 순환 참조가 일어날 수 있어 UITableView는 딜리게이트 프로퍼티를 약한 참조하고 있습니다. 그런데 약한 참조는 참조 타입만 사용하기 때문에 딜리게이트 패턴에 사용되는 프로토콜이 참조과 같이 동작할 필요가 있습니다.
따라서 프로토콜에 AnyObject를 채택하여 클래스 유형에서만 사용하는 프로토콜로 제한할 필요가 있습니다.
그러면 프로토콜은 만능일까?
딜리게이트 패턴만 봐도 프로토콜은 사용자 정의 타입처럼 사용 가능해 보입니다!
또한 의존성 역전을 구현하기 위해 프로토콜만한 기능이 또 없죠!
그러면 프로토콜은 만능일까요? 프로토콜을 사용자 정의 타입처럼 어디든! 막! 가져가 쓸 수 있는걸까요?
이 질문에 대해서는 프로토콜을 어디든 가져가 쓸 순 없다고 생각합니다.
1. 딜리게이트 패턴을 구현할 때 순환참조를 막기 위한 약한 참조로 프로토콜을 사용할 때
프로토콜은 클래스와 같이 참조 타입이다! 라고 말할 순 없습니다. 왜냐하면 프로토콜은 클래스 외에도 구조체와 열거형, 그리고 프로토콜 자체에도 채택할 수 있기 때문입니다.
그래서 weak 키워가 있는 변수에는 일반적인 프로토콜을 명시할 수 없습니다.
대신 프로토콜에 AnyObject 프로토콜을 프로토콜에 채택하면 참조 타입만 프로토콜을 채택할 수 있게 되며 약한 참조에 프로토콜을 명시할 수 있게 됩니다!
2. 선택적 프로토콜 요구사항을 위한 @objc 키워드를 사용할 때
기본적으로 프로토콜의 요구사항은 필수적입니다. 프로토콜을 채택한 타입은 내부에 요구사항들을 꼭 구현해야 하며 구현하지 않으면 경고가 뜨게 됩니다.
그런데 @objc 키워드를 붙인 요구사항은 선택적인 요구사항으로서 구현하지 않아도 경고가 뜨지 않습니다. 대신 @objc 를 붙인 요구사항은 NSObject를 상속받아야 하기 때문에 상속이 가능한 클래스만 프로토콜을 채택할 수 있습니다.
'Swift' 카테고리의 다른 글
[Swift] 로컬라이징(localizing)을 이용한 string 관리 (0) | 2022.03.25 |
---|---|
[Swift] 전처리문(The preprocessor macro) (0) | 2022.03.21 |
[Swift] Closure (0) | 2021.12.17 |
[Swift] Data(contentsOf: url) 동기적인 파일 다운로드 (0) | 2021.10.15 |
[Swift] URLSession으로 multipart/form-data request하기 (0) | 2021.09.27 |