Singleton Pattern은 클래스가 오직 하나의 인스턴스만 가지고 있음을 보장합니다. 프로그램 전반에 걸쳐 인스턴스를 공용으로 사용하고자 할 때나, 굳이 인스턴스를 여러 개 생성할 필요가 없을 때 사용합니다. 서버나 DB 혹은 입출력 장치와 같이 외부와 통신하는 역할을 수행하는 클래스에서 주로 사용합니다. UIKit에서도 자주 등장하는 패턴 중 하나입니다.
Singleton Pattern
은 GoF의 분류 체계에서 생성(Creational) 패턴에 속합니다.
Singleton Pattern은 단순히 인스턴스를 하나만 생성할 수 있는 클래스만 있으면 됩니다.
간단한 예제를 통해 Singleton Pattern에 대해 알아봅시다. 사용자 인증 정보를 관리하는 AuthService
를 구현했습니다. 프로그램 전반에서 해당 서비스에 접근하여 사용자 정보를 불러오거나 업데이트할 수 있습니다.
struct User {
var id: String
var name: String
}
final class AuthService {
private(set) var user: User?
func login(email: String, password: String) {
// 서버에 로그인을 요청하여 사용자 정보를 불러옴
user = User(id: "1", name: "jinyongp")
}
func logout() {
// 서버에 로그아웃을 요청
user = nil
}
func update(user: User) {
// 서버에 사용자 정보 변경을 요청
self.user = user
}
}
이렇게 Singleton이 아닌 방식으로 구현한다면 AuthService
로부터 사용자 정보를 불러올 때 사용자 정보가 매번 초기화됩니다.
// 로그인을 통해 사용자 정보 저장
let authService = AuthService()
authService.login(email: "dev.jinyongp@gmail.com", password: "PASSWORD")
print(authService.user?.name) // jinyongp
// 다른 곳에서 사용자 정보를 불러오고자 인스턴스를 생성하면 사용자 정보가 없음
let authService = AuthService()
print(authService.user?.name) // nil
이를 Singleton Pattern을 적용하여 해결해봅시다.
final class AuthService {
static let shared = AuthService()
private init() { print("인스턴스 생성") }
private(set) var user: User?
func login(email: String, password: String) {
// 서버에 로그인을 요청하여 사용자 정보를 불러옴
user = User(id: "1", name: "jinyongp")
}
func logout() {
// 서버에 로그아웃을 요청
user = nil
}
func update(user: User) {
// 서버에 요청
self.user = user
}
}
static
키워드를 활용하여 shared
라는 타입 속성을 추가했습니다. shared
는 AuthService
의 생성자를 실행하여 인스턴스를 얻습니다. 그리고 새로운 인스턴스 생성을 막기 위해 private init() {}
을 추가했습니다. 이제 외부에서 생성자를 호출할 수 없고, shared
속성으로만 인스턴스를 얻을 수 있습니다.
Swift에서 타입 속성은 lazy로 동작합니다. 즉, 타입 속성에 처음 접근할 때 생성자를 실행합니다. 그렇기에
shared
를 사용하지 않는다면 인스턴스가 생성되지 않습니다.
// 로그인을 통해 사용자 정보 저장
let authService = AuthService.shared
authService.login(email: "dev.jinyongp@gmail.com", password: "PASSWORD")
print(authService.user?.name) // jinyongp
// 동일한 인스턴스를 가지므로 다른 곳에서 사용자 정보를 볼러올 수 있음
let authService = AuthService.shared
print(authService.user?.name) // jinyongp
이제 AuthService
의 인스턴스는 하나만 생성됨을 보장하고 인스턴스의 데이터를 유지할 수 있습니다.