유명 오픈소스 라이브러리 처럼 Swift 확장 기능 만들기

Steve Kim
5 min readMar 27, 2022
https://www.xda-developers.com/download-iphone-se-3-2022-wallpapers/

시작하며

SwiftExtension은 다른 어떤 언어의 확장 기능보다도 자유도가 높고 강력하다. 하지만 높은 자유도로 인해 무분별하게 확장 기능을 남발한다면 스파게티 코드가 될 가능성이 크다.

이 아티클에서는 Swift ProtocolExtension을 활용해 유명 오픈소스 라이브러리 처럼 확장 기능을 구현하는 방법에 대해 소개해 볼 생각이다.

Motivation

필자는 평소 잘 만들어진 오픈소스 라이브러리 내부 구현을 살펴보는 습관이 있다. RxSwift 내부를 살펴보던 중 ReactiveReactiveCompatible의 구현을 보며 매우 감탄했다. 처음엔 RxSwift 만의 독특한 구현 방식으로 생각했지만, 이러한 구현은 일종의 패턴처럼 유명 오픈소스 라이브러리에서 흔히 볼 수 있었다.
e.g) Kingfisher

RxSwift 코드 살펴보기

RxSwift/Reactive.swift

위 구현을 클래스 다이어그램으로 그려본 모습이다. Swift 언어 특성에 딱 맞게 그릴수 있는 도구가 없어 완벽하게 그리지 못한 것을 감안하고 보자.

Class Diagram of Reactive and ReactiveCompatible

ReactiveCompatible 프로토콜이 Reactive 클래스를 합성하고 있는 단순한 구조이며, ReactiveBasegeneric 타입으로 선언하고 있다. Reactive 클래스는 Basegeneric 타입으로 합성하고 있다.

이것만 봐서는 정확히 어떤 동작이 이루어질 지 알기 어렵다.
다음 구현을 좀 더 살펴보자.

아하! 위 구현을 보니 좀 더 명확하게 드러난다. ReactiveCompatible 프로토콜이 Reactive 클래스 인스턴스를 생성할 때 BaseSelf(자신)를 주입하고 있다.

결과적으로 ReactiveCompatible의 어떤 구현체라도 자신이 Base로 주입된 Reactive 클래스의 인스턴스 rx를 속성으로 갖게되는 것이다.

독자는 “그게 뭐? 어쨌다는건데?” 라고 생각할 수 있다. 조급해하지 말고 좀 더 집중해 보자. 이제부터 이로 인해 어떤 것이 가능한지 매직을 볼 수 있을 것이다.

NSObject가 ReactiveCompatible을 구현하고 있다. 아시다시피 UIKit의 UIButton, UIView 등 대부분은 NSObject의 하위 클래스이다. 고로 UIButton도 ReactiveCompatible의 구현체이다.

3번 라인처럼 extension Reactive where Base: UIButton의 구현으로 UIButton에만 적용되는 Reactive 클래스 확장 기능을 작성 할 수 있다.

아래와 같이 rx 확장 기능을 특정 구현체에만 제공할 수 있는 것이다.

let button = UIButton()
button.rx.tap
.bind { print("tapped") }
.disposed(by: disposeBag)

이 단순한 원리로 우린 “확장에 열려있고 변경엔 닫혀있는” 깔끔한 코드를 작성할 수 있다. 또한 Reactive는 struct이며 rx 속성을 호출할 때마다 새로운 인스턴스를 생성해 전달하므로 불변성의 장점도 취할 수 있다.

응용해보기

iOS 애플리케이션 개발에 필수적으로 사용하고 있는 UIAlertController를 잘 알고 있을 것이다. 이를 사용하는 로직을 Reactive와 동일한 구조로 확장해 보겠다.

Alert.swift

RxSwiftReactive ReactiveCompatible과 동일하게 Alert AlerCompatible 을 생성하고 Alert의 인스턴스로 alert 속성을 선언했다.

AlertCompatibleextension을 선언하고, alert 속성의 getter엔 자신을 주입해 Alert 인스턴스를 생성하는 로직을 구현했다.

UIViewController+Alert.swift

UIViewController를 확장해 AlertCompatible을 구현하도록 한 후 필요한 함수를 추가했다. 이제 UIViewControllerUIAlertController를 간단히 띄울 수 있는 확장 기능을 갖추게 된 것이다.

아래와 같이 간단히 사용할 수 있다.

MyViewController.swift

만약 다른 클래스에서 Alert 기능이 필요하다면 해당 클래스에 AlertCompatible 을 구현하도록 한 후 필요한 함수만 추가하면 그만이다.

마치며

구현 예시를 통해 충분히 장점이 전달 됐을지 모르겠다. 필자는 꽤 오랫동안 이 방법을 사용해 왔는데 활용도가 매우 높았고 구조적으로도 매우 만족스러웠다. 오늘 소개한 확장 기능 구현 방법을 여러분의 프로젝트에도 도입해 보길 바란다. 분명 지금보다 더 나은 확장 기능 구현 방법이 될 것이다.

P.S. 잘 만들어진 유명 오픈소스 코드를 읽는 것만으로 많은 발전을 이룰 수 있다. 덤으로 그 속에서 보석을 발견할 수도 있을 것이다.

--

--