Swift/개념 & 응용

[Swift] Protocol 확장을 이용한 Singleton 개선

유정주 2023. 4. 2. 15:26
반응형

서론

싱글톤 패턴의 가장 큰 장점은 편리하다는 것입니다.

하지만 여러 부수적인 문제가 많고 특히, 사이드 이펙트가 크다는 점이 문제입니다.

프로젝트 전역에 두루두루 쓰이는 게 싱글톤 객체인데,

이말은 곧 싱글톤 객체에 문제가 생기면 프로젝트 전역에 문제가 생긴다는 뜻이기 때문입니다.

 

오늘은 Swift의 프로토콜 확장을 통해 이를 어느정도 해소할 수 있는 방법을 알아보겠습니다.

이것도 장단점이 있으니 프로젝트에 적절할 때 적용해보면 좋을듯 합니다.

 

프로토콜 확장(extension Protocol)

기본 원리는 프로토콜을 이용해 한 곳에서 싱글톤 호출을 담당하는 것입니다.

구조체나 클래스가 아니라, 프로토콜을 사용하는 이유는 싱글톤의 존재를 숨길 수 있기 때문입니다.

프로토콜을 정의하고, extension으로 기본 구현을 제공해서 이를 채택하는 것이죠.

 

예시

예시를 먼저 보고 장단점에 대해 알아보겠습니다.

저는 저번 포스팅에서 다뤘던 JeongLogger를 개선해보겠습니다.

(JeongLogger에 대한 내용은 JeongLogger SPM 라이브러리 생성과 배포에 있습니다.)

 

JeongLogger를 도입할 때의 문제는 아래와 같습니다.

  1. 프로젝트 전역에 JeongLogger 라이브러리를 import 해야함(os_log라면 OSLog를 import 해야함)
  2. JeongLogger 라이브러리는 "라이브러리"이므로 언제든지 문제가 발생할 수 있음
  3. JeongLogger 라이브러리에 문제가 생기면 프로젝트 전역에 문제가 생김

기능적으로도, 유지보수 관점에서도 문제가 있네요.

 

하지만 편하게 로그를 남기는 행위(?)를 포기할 순 없습니다.

 

그래서 Loggable이라는 프로토콜을 정의하고 여기에서 JeongLogger 라이브러리 메서드를 호출할거에요.

protocol Loggable {
    func log(_ level: OSLogType, _ log: String)
}

Loggable은 JeongLogger의 log 메서드를 호출할 메서드를 정의합니다.

 

프로토콜을 extension해서 기본 구현을 정의합니다.

extension Loggable {
    func log(_ level: OSLogType = .default, _ log: String) {
        JeongLogger.log(log, level: level)
    }
}

log 메서드에서는 JeongLogger의 log 메서드를 호출합니다.

 

호출부에서는 Loggable을 채택하기만 하면 위 log 메서드를 사용할 수 있습니다.

final class HttpService: Loggable {
    ...
    
    func requestGet(url: String) async -> Response {
        log(.default, url)
        ...
    }
}

 

제 프로젝트에는 모든 ViewController가 상속하는 BaseViewController 클래스가 있습니다.

class BaseViewController<LayoutView: UIView>: UIViewController, Loggable {    
    ...
}

이럴 경우 BaseViewController에서만 Loggable을 채택하면 모든 ViewController에서 log 메서드를 사용할 수 있죠.

 

장점

위에서 언급한 싱글톤의 단점을 해결할 수 있습니다.

 

한 곳에서 싱글톤을 관리하기 때문에 문제가 생겨도 한 곳만 수정하면 됩니다.

별도의 import가 필요한 경우에도 한 곳에만 추가를 하면 되므로 편리합니다.

 

확장도 자유롭습니다.

라이브러리인 경우 서브클래싱을 지원하지 않는 경우가 있습니다.

프로토콜을 확장해서 사용하는 경우, 라이브러리를 호출하는 곳에서 커스텀이 가능하기 때문에 어느정도 해소가 가능합니다.

예를 들어, JeongLogger 라이브러리는 디버깅 모드, 릴리즈 모드 상관 없이 모두 로그를 출력합니다.

디버그 모드에서만 출력하고 싶은 경우 JeongLogger 라이브러리를 사용하지 못하죠.

이럴 경우, 위 방법을 이용해 디버그 모드에서만 JeongLogger를 호출하면 되니 문제를 해결할 수 있습니다.

 

마지막으로 싱글톤이라는 것을 숨길 수 있습니다.

싱글톤 객체를 사용할 때는 어느정도 불안감을 가질 수 있습니다.

서론에서 말했듯 싱글톤은 부작용이 심한 패턴이기 때문입니다.

그래서 싱글톤 객체가 매우 안정적이라면 호출부에서 싱글톤이라는 것을 숨겨서 심적 부담감을 줄일 수 있습니다.

싱글톤 객체를 사용하기 위해 내가 멀티 스레드 문제 처리 등을 해줘야 하는가?를 고민하지 않아도 되기 때문입니다.

 

단점

순수한 싱글톤보다 불편하다는 단점이 있습니다.

싱글톤의 가장 큰 장점은 편리함인데 이게 약간은 감소하는거죠.

따라서 사용하는 싱글톤 객체의 특성을 보고 적절히 선택해야 합니다.

(이를 고민하는 과정에서 싱글톤 자체가 불필요하다고 판단될 수도 있겠죠?)

 

마무리

이번 포스팅에서는 Swift의 프로토콜로 싱글톤의 단점을 해소하는 방법을 알아보았습니다.

편리함을 해치지 않는 선에서 안정적인 구현을 챙길 수 있다는 장점이 있으니

프로젝트에 적합하다면 채택해 보세요.

 

감사합니다!


아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

 

 

반응형