
A: "싱글톤 패턴은 무엇인가요?"
B: "객체를 단 하나의 인스턴스로만 생성하여, 프로세스가 돌아가는 동안 전역에서 접근할 수 있도록 하는 디자인 패턴입니다."
A: "그렇다면 이 패턴의 위험성이나 단점에 대해 말씀해주세요."
B: "..."
개발자라면 누구나 싱글톤 패턴을 다뤄봤거나 알고 있을 것입니다.
하지만 "싱글톤 패턴의 위험성을 충분히 고민하고 쓰고 계시나요?"라는 질문에는 선뜻 답하지 못하는 경우가 많습니다. 편리함 속에 가려진 싱글톤의 이면과, 올바른 구현 방식에 대한 고민을 기록으로 남겨둡니다.
전 세계 개발자들의 교과서인 'Head First: Design Pattern'에서는 싱글톤을 다음과 같이 정의합니다.
싱글톤 패턴(Singleton Pattern)은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든 그 인스턴스에 접근할 수 있도록 하기 위한 패턴이다.
사용하기엔 참 편리하지만, 오남용 시 객체지향 아키텍처를 망치기도 합니다.
멀티스레드 환경(Thread-Safe)을 고려하며 발전해 온 싱글톤 구현 기법들의 장단점을 짚어보겠습니다.
companion object 내에서 호출할 때마다 새로운 객체를 반환한다면 싱글톤의 정의가 무너집니다.
class FakeSingleton private constructor() {
companion object {
// 호출할 때마다 새로운 객체를 인스턴스화함
fun getInstance(): FakeSingleton = FakeSingleton()
}
}
@Synchronized 키워드나 Double-Checked Locking을 사용하여 스레드 안전성을 보장하는 방식입니다.
class SynchronizedSingleton private constructor() {
companion object {
@Volatile
private var instance: SynchronizedSingleton? = null
@Synchronized
fun getInstance(): SynchronizedSingleton {
if (instance == null) {
instance = SynchronizedSingleton()
}
return instance!!
}
}
}
단점: Synchronized 블록은 동기화 락을 획득하는 과정에서 많은 비용(Cost)을 소모하므로 성능 저하의 주원인이 됩니다.
호출되는 시점에 초기화(Lazy Initialization)되며, 코틀린 내부적으로 스레드 동기화(Thread-safe)를 기본 보장합니다.
class LazySingleton private constructor() {
companion object {
val instance: LazySingleton by lazy { LazySingleton() }
}
}
특징: 기본적으로 LazyThreadSafetyMode.SYNCHRONIZED로 동작하므로 멀티스레드 환경에서도 안전합니다. (다만 모드를 명시적으로 변경 시 원자성이 깨질 수 있습니다.)
JVM의 클래스 로딩 시점을 이용한 전통적이고 가장 우수한 기법입니다. JVM이 클래스를 초기화하는 과정에서 원자성을 보장하는 특성을 활용합니다.
class HolderSingleton private constructor() {
companion object {
val instance: HolderSingleton
get() = Holder.INSTANCE
}
private object Holder {
val INSTANCE = HolderSingleton()
}
}
장점: HolderSingleton 클래스가 로드될 때는 Holder가 모르는 상태였다가, instance가 최초로 호출되는 시점에만 내부 Holder 클래스가 로드되며 인스턴스가 생성됩니다. 구조적으로 Thread-safe 하며 성능 저하가 없습니다.
가장 좋은 방법은 코틀린이 언어 차원에서 제공하는 object 키워드를 사용하거나, 수동 싱글톤보다는 Hilt, Koin 같은 DI(의존성 주입) 프레임워크를 활용하여 객체의 생명주기를 컨테이너가 싱글톤으로 관리하도록 위임하는 것입니다.
객체를 싱글톤으로 설계하기 전, "이 객체가 정말 전역 상태를 가져야만 하는가?"를 스스로에게 꼭 질문해 보아야 합니다. 구조에 대한 깊은 고민 없는 싱글톤의 무분별한 사용은 결국 거대한 기술 부채로 돌아온다는 점을 명심해야겠습니다.