[Design Pattern] 내가 보려고 정리하는 Swift 디자인 패턴 (1) - 싱글톤 패턴(Singleton Pattern)

2024. 2. 19. 22:06Architecture, Design Pattern

1️⃣ static : 타입 프로퍼티 (Type Property) 개념 정리

Swift의 static 키워드는 인스턴스 생성 여부와 상관없이 무조건 1개의 값만 존재하는 프로퍼티를 선언할 때 사용한다.

객체 지향 프로그래밍에서 처음 내가 어렵게 느꼈던 부분은 한 행위 또는 내용물에 대해 여러 표현법을 사용한다는 점이었다.
그래서 이 글을 읽는 다른 사람들은 과거의 나 같은 어려움을 느끼지 않았으면 하는 마음에서, 용어를 먼저 정리해보고자 한다.

  • 인스턴스 = 클래스 혹은 구조체에서 찍어낸 결과물 = 붕어빵 틀에서 만들어진 붕어빵 = 객체 (매우 중요!)
  • 생성 = 클래스 혹은 구조체에서 결과물을 찍어냄 = 변수 또는 상수에 클래스/구조체를 기반으로 한 값을 대입함 = 클래스/구조체에서 비롯된 객체가 메모리에 실제로 할당됨
  • 프로퍼티 = 인스턴스 안에 들어있는 값 = 클래스 혹은 구조체 객체에 들어있는 정보 = 변수 혹은 상수에 할당된 값
  • 무조건 1개의 값만 존재 = 여러 개의 인스턴스가 있어도 값은 동일 = 객체는 여러 개여도, 그 객체에 들어있는 정보는 모두 같다.

즉, 위 용어들을 바탕으로 static 키워드를 다시 풀어서 정리해보면,
Swift의 static 키워드는 여러 개의 객체가 만들어지더라도 모두 같은 정보(값)를 가지도록 하는 프로퍼티를 만들 때 사용한다고 표현할 수 있다.


앞으로는 static 키워드를 붙인 프로퍼티를 타입 프로퍼티 (Type Property)라고 부를 것이다.
생성된 각각의 인스턴스와는 상관없이 그저 타입(class, struct, enum 같이 객체를 찍어내는 큰 틀을 생각하면 되겠다.) 자체에 속해 한 가지 값만 가지기 때문이다.

타입 프로퍼티는 저장 프로퍼티(let, var 모두 가능)와 연산 프로퍼티(var만 가능)에 모두 적용할 수 있다.
이때, 저장 타입 프로퍼티의 경우에는 초기값을 반드시 넣어줘야 하며, lazy를 붙이지 않더라도 첫 접근 시 느리게(lazy) 초기화되는 것이 특징이다.

💡 저장 타입 프로퍼티와 초기화
: 저장 타입 프로퍼티의 경우 값을 할당할 initializer가 없기 때문에 항상 타입을 선언할 때 같이 값을 지정해줘야 한다.

이는 타입으로부터 인스턴스가 생성될 때마다 같이 생성되는 기존 프로퍼티와는 다르게,
해당 타입을 한번 사용해서 메모리에 올라갈 경우 같은 메모리 위치에 언제 어디서든 접근가능해지기 때문에, 다른 해당 타입의 인스턴스를 생성하더라도 다시 생성되지 않는 static의 특징에서 비롯된다고 보면 되겠다.

저장 타입 프로퍼티에서 초기값을 할당하지 않으면 초기값을 할당하던지, 연산 프로퍼티로 생성하라는 에러를 발생

위에서 계속 이어져서 타입 프로퍼티는 결국 "타입 자체에 속하는 프로퍼티"라는 속성에서 비롯되어,
"인스턴스.프로퍼티"로 접근할 수 없고 "타입이름.프로퍼티"로만 접근할 수 있는 것이 타입 프로퍼티의 특징이다!

 

2️⃣ 싱글톤 패턴 (Singleton Pattern) 개념 정리

싱글톤 패턴(Singleton Pattern)이란 하나의 객체를 가지고 공용으로 사용할 수 있도록 만드는 디자인 패턴이다.

싱글톤 패턴이 하나의 객체를 가지고 공용으로 사용하는 이유는 "객체를 굳이 여러 개 만들 필요가 없기 때문"이다.

예시를 들어보겠다.
사용자의 이름, 나이, 지역을 담고 있는 클래스가 있다고 가정해 보자.
그런데 이름을 받고, 나이를 받고, 지역을 받는 뷰컨트롤러(클래스)가 각각 구분되어있다고 한다면, 아래와 같이 각각의 뷰컨마다 인스턴스를 만들고 지정하고 싶은 프로퍼티에 접근해서 값을 담아야 할 것이다.

딱 봐도 느껴지는 이 비효율성....

이런 경우가 위에서 말했던 "객체를 굳이 여러 개 만들 필요가 없는 경우"에 해당하며,
이를 해결하기 위해 "하나의 객체를 가지고 공용으로 사용"할 수 있도록 만드는 디자인 패턴인 싱글톤 패턴(Singleton Pattern)이 등장하게 된 것이다.


느낌이 온 사람도 있겠지만, 위에서 다룬 Swift의 타입 프로퍼티(static)는 싱글톤 패턴의 목적과 동일하다고 볼 수 있다.
그렇기 때문에 Swift에서 Singleton Pattern은 static을 활용해서 구현한다.
*또한 (정확하게 구현하기 위해) init 함수로 또 다른 인스턴스 생성을 막기 위해 생성자의 접근 제어를 private 수준으로 지정해줘야 한다.

위와 같이 타입 프로퍼티를 생성하면,
각 뷰컨에서 별도의 인스턴스를 만드는 것이 아니라 하나의 인스턴스를 공유하며 접근하는 것이 된다!

User 클래스 속 shared라는 타입 프로퍼티에 모두 접근하는 것이다!

 

3️⃣ 싱글톤 패턴 (Singleton Pattern), 그래서 뭐가 좋은 거고, 어디에 쓰이는데?

딱 봐도 느껴지겠지만, 불필요한 메모리 낭비를 방지할 수 있다는 점이 가장 큰 장점이다.
인스턴스를 여러 개 만들지 않아도 되는 점에서 불필요한 메모리 낭비를 줄일 수 있다는 점도 있지만, static의 lazy 특성에 따라 생성 시점에 메모리에 할당되고 사용하지 않을 때 해제가 발생한다는 점에서도 불필요한 메모리 낭비를 막을 수 있다.

하지만, 멀티 스레드 환경에서 동시에 싱글턴 객체를 참조할 경우 원치 않은 결과를 가져올 수도 있으며, (이는 추가적인 게시물로 다뤄보겠다..)
한 싱글톤으로 선언된 인스턴스가 너무 많은 곳에 결합되어 있다면, 이것을 찾기도 어떤 작업을 하는지도 찾기 어려워지는 OOP의 기본원칙을 위배하는 상황이 발생할 수도 있을 것이다.

어떤 디자인 패턴을 활용하더라도 항상 긍정적인 면과 위험성을 함께 고려하여 활용해야 한다는 것을 잊지 말자.

그럼에도 불구하고, 싱글톤 패턴의 장점은 너무나도 명확하기 때문에 iOS 안에서도 여러 방면에 사용되고 있다.

예를 들면 환경설정, 네트워크 연결처리, 데이터 관리 같이 하나의 객체를 계속 사용하는 곳에서 말이다.
특히, 예전 글에서도 다뤄본 적이 있는 userDefaults도 싱글톤 패턴에 따른 결과물이었다! (은연중에 사용하고 있던 싱글톤..)