Swift: Singleton, 싱글톤 패턴

Heechan
HcleeDev
Published in
8 min readAug 18, 2021
Photo by Scott Graham on Unsplash

가끔 개발을 하다보면 어떤 라이브러리는 shared , defaults 같은 이름을 가지고 있어서, 이 라이브러리는 왜 굳이 프로퍼티나 메서드 접근을 이렇게 하도록 만들어뒀지 궁금했던 적이 있다.

그 이유를 최근에서야 알게 되었는데, 그런 경우 Singleton 패턴을 적용해 만들었다는 것이었다.(심지어 한국어로 된 관련 문서를 찾아보기 전까지는 싱글레톤으로 읽고 있었다) 그래서 이번주는 Singleton 패턴에 대해 간단히 소개해보고자 한다.

Singleton

싱글톤, 이름으로부터 알 수 있듯 어떤 것을 ‘하나’로 유지한다는 느낌이 강하게 온다. 수학적 용어로는 한원소 집합을 표현할 때 사용되는 단어다. 말그대로 원소가 하나만 있는…

위키피디아에 따르면 Singleton을 이렇게 소개하고 있다.

The singleton pattern is a software design pattern that restricts the instantiation of a class to one “single” instance.

한 개의 클래스로 만드는 객체는 단 한개여야만 한다는 규칙을 가진 디자인 패턴이다.

프로그램 전체에 단 하나의 전역 객체를 만들어놓고 여기저기서 이 하나의 객체에만 접근할 수 있도록 하면 된다.

이렇게만 말하면 뭔지 잘 몰라도 iOS 개발을 했다면 계속 마주쳤을 것이라고 생각한다.

URLSession.shared : 네트워크 처리를 할 때 URLSession 객체를 이용하는데, 이미 만들어져있는 shared 객체에 접근해 메서드를 수행한다.

UserDefaults.standard : UserDefaults에서 기본적으로 공유하고 있는 standard 저장소에 접근해 쓰고 읽기 위해 만들어져있는 standard 에 접근해 메서드를 수행한다.

이런 친구들이 다 싱글톤 패턴을 기반으로 만들었다고 생각하면 된다.

우리가 실제로 구현한다면 이런 식으로 만들 수 있다.

class SingleTonExample {
static let shared = SingleTonExample()
private init { }
}

static 을 이용해 전역적으로 사용할 수 있도록 만들고, private init 을 통해 다른 곳에서는 SingleTonExample 을 초기화, 객체로 만들 수 없다.

이렇게 만들면 이 프로그램 전체에서 SingleTonExample 객체는 이거 하나밖에 없다고 볼 수 있다. 멀티 스레드 상황 같은 특수한 상황에서는 반드시 그렇다고 장담할 수는 없다고 하지만, 일반적으로는 Single이라고 보면 된다.

그러면 싱글톤 패턴을 이용해서 좋은 점은 무엇이 있을까?

  • 메모리를 단 한번만 사용한다.

해당 객체는 단 한번만 만들어지기 때문에 메모리 관리가 편하다는 장점이 있다. 한번 만들어둔 객체를 꾸준히 재사용할 수 있다. 원래 class로 만드는 객체는 모종의 이유로 메모리가 풀리지 않는 경우에 Memory Leak을 찾느라 고생하는 일도 있는데, 적어도 그런 일은 없을 것이다.

  • 객체 접근 시간이 줄어든다

다시 메모리를 할당하고, 초기화(init)하는 과정과 시간이 줄어드니, 한번 만들어두기만 하면 다시 접근할 때는 매번 객체를 만들 때보다 조금이나마 더 빠르다.

  • 전역 범위에서 상태, 데이터 전달이 쉬워진다

어디서든 접근할 수 있기 때문에 어떤 상태를 표시하거나, 데이터를 공유하기에 적절하다. 단 하나의 객체만 있고, 잘 신경써서 동시성 문제가 생기지 않도록 짜기만 한다면 걱정 없이 상태나 데이터를 전달할 수 있다.

하지만 단점도 상당히 많다.

다들 Singleton을 싫어하나보다… What are drawbacks or disadvantages of singleton pattern? — Stack Overflow

위에서 말한 메모리, 객체 접근이 막 엄청난 장점이라고 보기는 어렵다. 당연히 메모리 관리를 잘 해야 하긴 하지만 굳이 이렇게 제한적인 규칙으로 잡을 필요는 없다. 또한, 이 싱글톤을 사용하는 가장 큰 이유가 3번째 장점인 상태 공유와 데이터 전달인데, 전역 변수, 객체를 사용하는 것 자체가 단점이 많다는걸 생각해보면 단점이 분명히 있다.

  • 테스트가 힘들다

싱글톤 class는 위에서 살짝 봤던 것처럼 initprivate 로 설정해 유일성을 보장하는 경우가 많다. 그런 의도에서야 좋지만 이렇게 생성을 제한해버리면 테스트용 Mock 객체를 만들어내기 어려워진다. 이러면 관련된 로직을 테스트하기 어려워지고, 테스트 코드가 중요한 경우에는 꽤 곤란한 존재가 될 수 있다.

  • 의존성을 만들어낸다

‘여기저기’서, ‘쉽게’ 접근할 수 있는 탓에, 프로젝트 이곳저곳에서 상상 이상으로 사용될 수 있다. 이러면 우리가 어떤 객체와 연결되어있는지 다 찾아내기도 힘들어진다.

  • 멀티 스레드 환경에서 위험할 수 있다

멀티 스레드 환경에서 어쩌다보니 싱글톤 객체가 2개가 생기는 문제가 발생할 수 있다. 또한 싱글톤 객체 내부의 데이터를 동기적으로 처리하지 않으면 여러 객체가 동시에 사용하다가 꼬여버릴 가능성도 있다.(이런 부분은 예전에 다루었던 Swift의 새 기능 actor를 잘 사용하면 괜찮을 수도…) 이런 점을 주의하지 않으면 싱글톤의 장점이 많이 희석될 것이다.

따라서 싱글톤 객체를 사용하고자 할 때는, 안타깝지만 가급적 사용하지 않는 방법을 먼저 고민해보는 것이 좋을 것 같다.

그래도 사용하는 것이 좋을 것 같으면, 동시성에 주의해 설계하고 싱글톤 객체가 최대한 가볍게 유지되도록 하는 것이 좋다. 역할과 책임을 최소한으로 하고, 정말 딱 필요한 것들만 가지고 있음으로써 필요 이상으로 다른 객체들에게 영향을 주지 않도록 해야 한다.

실제 사용 예시

어떤걸 만들어볼 수 있을까 고민하다가, 약간 URLSession 느낌으로 뭔가 네트워크 작업을 하는 경우를 생각했다.

예를 들어 우리 앱에서 서버에 요청을 할 때는 동시에 반드시 하나의 요청만 할 수 있어야 한다고 생각해보자.

다른 좋은 방법도 많겠지만, 싱글톤 패턴을 통해 만들어보자고 생각했다. 지금 우리 앱에는 외부와 소통할 수 있는 공중 전화 박스가 하나만 있고, 한 사람 씩 사용할 수 있는 상태가 되었으면 좋겠다는 가정이다.

출처: Semaphores (keil.com)

뭐 자세히 설명하진 않겠지만 Lock과 비슷한 Semaphore를 사용하고자 하는데, Semaphore는 공유된 자원에 동시에 얼마나 접근할 수 있도록 할지 정할 수 있는 방법이라고 볼 수 있다.

중구난방으로 발생되는 이벤트들을 동기적으로 실행하고 싶을 때 사용하는 기능으로, iOS 개발에서는 DispatchSemaphore 가 있다. wait 를 사용하면 공유할 수 있는 자원이 생길 때까지 멈춰있고, signal 을 사용하면 쓰고 있던 자원을 이제 풀어준다는 신호를 보낼 수 있다.

이를 이용해서 아주 간소화한 싱글톤 객체를 만들어보자.

shared 로 단 하나만 만들어지는 싱글톤 객체를 만들었다. 또한 동시에 공유할 수 있는 자원을 하나만 가진 DispatchSemaphore를 semaphore 에 저장해줬다.

send 는 데이터를 가지고 네트워크 상에서 통신하는 역할을 하고, 작업 완료시 completionHandler 를 실행한다.

sendNetworkRequestpublic 으로 설정되어, 다른 객체들에서는 네트워크 요청을 하기 위해서 NetworkSingleton.shared.sendNetworkRequest(data: Data) 를 작성해야 한다.

여기서는 semaphore 가 남는 자원이 생길 때까지 기다리고, 생기면 취득한 뒤 작업을 시작한다. 그 후 completionHandler에서 semaphore.signal() 을 통해 자원을 풀어줄 수 있다.

위 코드가 완벽한건 아니긴 한데, 대충 이런 식으로 구성한다면 여러 스레드에서 NetworkSingleton에 접근하더라도 동시성 문제도 생기지 않고, 한번에 한 요청만 처리될 수 있도록 보장할 수 있다.

결론

Singleton 객체를 만들 때는 충분한 생각이 필요하다는걸 느낄 수 있었다. Singleton을 통해 얻을 수 있는 장점이 단점보다 훨씬 크고, 단점에서 우려하는 점을 최대한 해결할 수 있다는 생각이 들 때만 사용해야겠다는 생각은 드나… 워낙 편리한건 사실이라 고민이 되긴 한다.

지금 보니 Semaphore같은건 학부 OS 수업 들을 때 많이 건드렸던건데… 다음에 iOS의 GCD나 이런 개념들도 블로그에서 다뤄보면 괜찮을 것 같다.

참고한 것

Let’s examine the pros and cons of the Singleton design pattern (freecodecamp.org)

Are Singletons Bad (cocoacasts.com)

--

--

Heechan
HcleeDev

Junior iOS Developer / Front Web Developer, major in Computer Science