Swift Protocol 가지고 놀기 :-)

Protocol 의 세계는 신기하기도 이상하기도…

Jay Kim
8 min readAug 14, 2021
Photo by erik_stein on Pixabay

Swift 의 Protocol 은 사용방법이 무궁무진해서 동일한 동작을 수행하는 기능이라도 개발자마다 저마다의 스타일로 코드를 작성할 수 있어서 좋기도 하지만, 그 다양한 사용방법 때문에 코드가 읽기 어려워지거나 확장성이 줄어드는 등의 단점이 발생하기도 한다.

오늘은 Protocol 의 기본적인 사용법이 아닌 Protocol 의 다양한 특성에 대해서 한 번 이야기해보려고 한다.

1. Class 에서만 사용가능한 Protocol

기본적으로 Protocol 은 Class 와 Struct 어디서든지 사용 가능하다.
아래 DummyPrint 라는 Protocol 을

(1) Class
(2) Struct

양쪽에 적용을 해보겠다.

Playground 에서 아래와 같은 코드를 작성하고 실행시키면 결과가 예상대로 잘 나온다.

만약 여기서 DummyPrint 를 Class 에서만 사용 가능한 Protocol 이라고 한정시키고 싶으면 어떻게 하면 될까?

Protocol 을 정의할 때 class 혹은 AnyObject 에 준수(confirm) 시키면 아래와 같이 해당 Protocol 을 Struct 에서 사용시에 에러가 발생하는 것을 볼 수 있다.
SwiftFormat 등의 Formatter 등을 사용하면 저 class 부분을 자동으로 AnyObject 로 바꿔주기도 하니, 처음부터 AnyObject 를 사용하는 습관을 들이면 좋다.

class 에 적용시킬 경우 warning 이 발생하는데
AnyObject 에 적용시키면 warning 이 사라진다

이렇게 class 에서만 사용하는 Protocol 을 적용하는 방식으로 struct 에서는 해당 Protocol 을 사용할 수 없도록 하는 것이 가능하다.

2. Protocol 의 기본동작 구현 (default implementation)

사실 Objective-C 시절에는 아래처럼 Protocol 을 정의할 때 내부 함수들이 required 인지 optional 인지 간단하게 keyword 로 정의가 가능했었다.

위와 같이 정의했을 때 해당 Protocol 적용 시에 required 키워드가 붙은 녀석들만 구현해주면 optional 키워드가 붙은 함수들은 구현을 해주지 않아도 상관이 없었다.

Swift 에서는 이것이 조금 더 엄격한 방식으로 바뀌어서 기본적으로는 아래처럼 Protocol 적용만 하고 구현을 해주지 않는 경우 에러가 발생한다.
(Swift 에서는 Objective-C 에서처럼 optional 키워드를 바로 사용할 수는 없다.)

그렇다면 Swift 에서도 Protocol 함수들을 정의할 때 필수적으로 구현하지 않아도 되도록 하려면 어떻게 하면 될까?

방법은 크게 두 가지가 있다.

(1) Protocol 함수의 기본-구현체(default implementation)를 만든다 : extension 사용

아래처럼 Protocol 의 Extension 에서 Protocol 에 정의한 형태와 동일한 함수를 구현해주고 내용을 비워주면, 실제로 Protocol 을 적용하는 부분에서 구현을 해주지 않아도 에러가 발생하지 않는다.

또한 Protocol 함수 기본-구현체의 내용을 채워줄 경우, Protocol 적용부에서 구현을 해주지 않고 호출할 경우 기본 구현체의 내용이 실행되는 것을 알 수 있다.

(2) @objc optional 을 사용

Protocol 정의시에 @objc 키워드를 사용 해주고, 함수명 앞에 @objc optional 키워드 를 사용해주면 Swift 에서도 optional 형태의 Protocol 함수를 정의해줄 수 있다.

하지만 위 화면에서 볼 수 있는 것처럼 Struct 에 이렇게 정의한 optional protocol 을 적용해주면 에러가 발생하는데, Apple 공식 문서에서는 다음과 같이 설명하고 있다.

@objc 키워드를 사용하게 되면, Objective-C Class 를 상속하거나 다른 @objc 키워드가 붙은 Class 를 상속한 경우에만 사용할 수 있다.

결국 @objc 키워드를 사용한 경우 에는 Struct 나 Enum 등에는 해당 Protocol 을 적용할 수 없다는 이야기다.

그래서 본인은 특별한 경우가 아니면 Protocol 기본 구현체 방식을 선호하며 즐겨 사용한다.

3. Protocol Composition

Protoocol 을 Class 나 Struct 에 적용할 때 여러개의 Protocol 을 한 번에 적용하고 싶을 경우 콤마(,) 로 구분하면 쉽게 적용할 수 있다.

하지만 만약 함수의 파라미터 속성에 여러 개의 Protocol 을 적용하려고 해도 콤마(,)로 가능할까?

위 화면과 같이 함수 파라미터에 여러 개의 Protocol 을 적용하려고 하는 경우 콤마(,)로는 사용이 불가능하다.

이럴 때 사용되는 것이 Protocol Composition 인데 간단하게 이야기하면 여러 개의 Protocol 을 앰퍼샌드(&)로 연결해주면 된다.

또 하나 재밌는 점은 Protocol Composition 은 Protocol 조건 이외에도 Super-Class 조건을 섞는 것도 가능하다는 점이다.

위의 예제를 보면 testFunc 의 target 파라미터는 TestClass 의 Sub-Class 이면서 DummyPrint Porotocol 을 적용한다는 조건을 Protocol Composition 으로 구현해 놓았다.

요약하면 Class, Struct, Enum 등을 정의할 때를 제외하고 파라미터 전달이나 Constraints 를 설정하는 곳에서는 Protocol 이나 Super-Class 조건을 설정해줄 경우에는 앰퍼샌드(&)로 각 조건들을 연결해서 사용하면 된다!!! 정도로 생각하면 좋을 것 같다. :-)

조금 더 내용을 찾아보고 싶다면, Swift 공식문서에도 Protocol Composition 관련 내용이 설명되어 있다.

4. Protocol 함수에서의 parameter 기본값 사용하기

Protocol 함수에서 parameter 의 기본값을 넣어주려고 하면 아래와 같이 에러가 발생한다.

그럼 어떻게 해야 가능할까?…
이번에도 extension 을 사용하면 Protocol 함수에서 parameter 에 기본값을 넣어줄 수 있다.

위처럼 Protocol extension 내에서 동일한 모양의 함수를 정의해주되 parameter 의 기본값을 넣어주고, 실제 내부 구현에서는 해당 기본값을 이용해서 원래의 Protocol 함수를 실행시켜주면 된다.

하지만 이렇게 parameter 기본값을 넣어준 함수는 기존의 함수와는 별개의 함수로 생각해야 한다. (기본 구현체가 아님!!!!)

이것을 Protocol 함수의 기본 구현체라고 생각하고 위처럼 사용하면 에러가 발생한다.

parameter 기본값을 가진 함수는 protocol 적용한 곳에서의 구현체가 존재해야 호출이 가능하므로 아래와 같이 처리해줘야 한다.

Swift 에서는 Protocol 로 할 수 있는 것들이 정말 많기는 하지만, 그만큼 사용시에 잘 사용해야 Protocol 이 가진 효용성을 충분히 활용할 수 있게 되는 것 같다. (뭐든 안그렇겠냐마는…)

다음에는 쉬우면서도 많은 분들이 정확하게 이해하지 못하고 사용하는 UINavigationController 에서의 view-stack 과 Modal 방식으로 present() 함수 사용시에 발생하는 presentingViewController, presentedViewController 의 관계에 대해 다뤄보려고 한다.

여기까지 읽어주셔서 감사하고,
모든 iOS 개발자에게 행복 있기를~

--

--

Jay Kim

iOS를 사랑하는 Software Engineer, 반복 설명하는 것이 점점 귀찮아져서 글을 씁니다 ^^;;;;