Notice
Recent Posts
Recent Comments
Link
Tags
- UILabel
- iTerm
- TOSS
- Swift
- GOF
- github
- LanguageGuide
- IOS
- WWDC
- UIKit
- Accessibility
- CollectionView
- lineBreakMode
- HIG
- 전달인자 레이블
- Split View
- 스위프트
- orthogonalScrollingBehavior
- 디자인패턴
- lineBreakStrategy
- iPad
- 야곰아카데미
- 앱개발
- Human Interface Guidelines
- 애플사이다
- Apple
- 애플
- DiffableDataSource
- Keychain
- Combine+UIKit
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Today
- Total
애플사이다의 iOS 개발 일지
[디자인 패턴] Singleton 본문
문제 상황
- 현재 로그인한 사용자의 id, nickname, email 등의 User Data는 어떻게 관리하는 게 좋을까?
- 앱을 사용하는 동안 로그인 상태가 수시로 변경될 수 있으므로 여러 화면에서 접근할 수 있어야 한다. 예를 들어 앱을 최초실행 했을 때 로그인화면에서 sign in 할 수 있고, 로그아웃 화면에서 sign out 할 수 있고, 다시 비로그인 상태에서 좋아요 버튼을 눌렀을 때 modal로 띄워지는 로그인 화면에서 sign in 할 수도 있다.
- 이때 여러 화면에서 어떠한 시점에 접근하더라도 로그인 상태는 동일해야 한다. 따라서 공유 리소스 형태로 관리하고 싶다.
- 위 상황에서 Singleton 패턴이 해결책이 될 수 있다.
패턴 설명
- 클래스의 인스턴스를 1개만 만들고, 이 인스턴스에 대해 여러 화면에서 접근하도록 한 패턴이다.
- 의문 - 전역변수를 쓰면 되지 않나? : 예상치 못한 시점에 인스턴스를 덮어쓰기할 수 있어서 위험하다.
(싱글턴은 만들어둔 인스턴스가 덮어쓰기되지 않도록 보호 가능)
- 의문 - 전역변수를 쓰면 되지 않나? : 예상치 못한 시점에 인스턴스를 덮어쓰기할 수 있어서 위험하다.
- 위 그림에서는 getInstance() 메서드를 통해 인스턴스를 반환하도록 했지만,
Swift 문법에 따라 static 프로퍼티를 선언하여 항상 똑같은 인스턴스를 반환하도록 해도 된다. - default initializer를 private로 설정하여 외부에서 인스턴스를 추가 생성하지 못하도록 방지한다.
장점
- 클래스가 단 1개의 인스턴스만 가지는 것을 보장한다.
- 인스턴스에 대한 전역 접근이 가능하다.
- 메모리 사용 측면에서 효율적일지도 모른다. (아래 단점 참고) Swift에서 static 프로퍼티는 최초 접근할 때 lazy한 방식으로 초기화되기 때문이다.
단점
Singleton은 다른 디자인 패턴에 비해 단점이 많다. 그래서 Singleton은 디자인 패턴이 아니라는 말까지 있다.
따라서 아래의 단점에 유의해서 사용해야 한다.
- 단일책임 원칙을 위반한다. 한 객체가 여러 가지 역할을 담당한다.
(1개의 인스턴스만 생성하도록 제어하는 역할을 하면서, 동시에 User Data를 관리하는 역할을 하는 등 두 가지 기능을 동시에 한다는 뜻인듯?) - 코드 간 결합도가 높아진다. 너무 많은 데이터를 공유할 경우, 개방폐쇄 원칙을 위반한다. 추적이 어렵다.
- 테스트 코드 작성이 어렵다. (공유 리소스이므로 테스트 코드를 위한 목적만으로 사용하는 게 불가능함. 또한 해당 클래스를 상속하는 Mock 객체를 초기화하는 것도 불가능함)
- 메모리 사용 측면에서 비효율적이다. static 프로퍼티에 인스턴스를 저장하므로 앱의 lifecycle 동안 메모리에 해당 인스턴스를 올려서 사용하기 때문이다.
- 다중 스레드 환경에서 여러 스레드가 인스턴스를 여러 번 생성하지 않도록 스레드 lock이 필요할 수 있다.
예시 코드
class UserManager {
static let shared = UserManager() // 단 하나의 인스턴스를 공유하여 사용함
private var id: Int?
private init() { } // 외부에서 인스턴스를 추가 생성 못하도록 방지
func saveId(_ id: Int) {
self.id = id
}
func deleteId() {
self.id = nil
}
}
// Client
// sign in 하는 시점
UserManager.shared.saveId(100)
// sign out 하는 시점
UserManager.shared.deleteId()
초기화 과정이 복잡하다면 약간 복잡한 아래 방법도 가능하다.
class NetworkManager {
// MARK: - Properties
// 클로저를 통해 복잡한 형태의 초기화가 가능함
private static var sharedNetworkManager: NetworkManager = {
let networkManager = NetworkManager(baseURL: API.baseURL)
// Configuration
// ...
return networkManager
}()
let baseURL: URL
// MARK: - Initializers
private init(baseURL: URL) {
self.baseURL = baseURL
}
// MARK: - Accessors
class func shared() -> NetworkManager {
return sharedNetworkManager
}
}
Reference
- Blog - Cocoacasts > Are Singletons Bad
- Blog - Cocoacasts > What Is a Singleton and How To Create One In Swift
'프로그래밍 철학' 카테고리의 다른 글
[toss] iOS 개발자를 위한 SLASH23 리뷰 (7) | 2023.06.18 |
---|---|
[toss] iOS 개발자를 위한 SIMPLICITY23 리뷰 - 디자이너와 친해지기 (7) | 2023.06.04 |
[디자인 패턴] Builder - 초기화 과정이 복잡할 때 (0) | 2023.02.20 |
[디자인 패턴] Abstract Factory - 미리 정해둔 종류로만 객체를 생성할 때 (0) | 2023.01.16 |
[디자인 패턴] Factory Method - 비슷한 종류의 타입을 찍어낼 때 (0) | 2023.01.14 |
Comments