소프트웨어 디자인 패턴 중 하나인 싱글톤 패턴(Singleton Pattern)은 특정 클래스의 인스턴스, 즉 객체가 애플리케이션 내에서 단 하나만 생성되는 것을 보장하는 설계 방식입니다. 생성자가 여러 번 호출되더라도 실제로 생성되는 객체는 최초에 생성된 하나이며, 이후의 모든 호출에서는 최초 생성된 객체를 반환합니다.
이는 마치 우리 반에 반장은 단 한 명만 존재하는 것과 같습니다. 누구든 "반장 나와!"라고 외치면 항상 같은 그 한 명의 반장이 나오는 것과 같은 원리입니다.
왜 싱글톤 패턴을 사용할까요?
싱글톤 패턴은 주로 다음과 같은 목적을 위해 사용됩니다.
메모리 낭비 방지: 객체를 생성할 때마다 메모리를 할당받게 됩니다. 만약 여러 곳에서 동일한 역할을 하는 객체가 필요할 때마다 새로운 객체를 생성한다면 불필요한 메모리 소모가 발생합니다. 싱글톤 패턴을 사용하면 최초 한 번만 객체를 생성하고 이후에는 계속해서 재사용하므로 메모리를 절약할 수 있습니다.
데이터 공유 용이: 단 하나뿐인 객체이므로, 애플리케이션의 여러 부분에서 해당 객체의 데이터를 공유하고 상태를 일관성 있게 관리하기 용이합니다. 예를 들어, 애플리케이션 전체의 환경 설정을 관리하는 객체나, 데이터베이스 연결을 관리하는 커넥션 풀(Connection Pool) 같은 경우에 유용하게 사용될 수 있습니다.
전역적인 접근성: 싱글톤 객체는 전역 변수처럼 애플리케이션의 어느 곳에서나 쉽게 접근하여 사용할 수 있습니다. 이를 통해 다른 객체들과의 상호작용이 편리해집니다.
싱글톤 패턴을 구현하기 위해서는 다음의 두 가지 핵심 요소를 지켜야 합니다.
class Singleton:
_instance = None # 유일한 인스턴스를 저장할 클래스 변수
def __new__(cls):
# 인스턴스가 아직 없으면 새로 생성
if cls._instance is None:
cls._instance = super().__new__(cls)
print("싱글톤 객체를 새로 생성했습니다.")
else:
print("기존 싱글톤 객체를 반환합니다.")
return cls._instance
def __init__(self):
# __init__은 __new__ 호출 후 매번 호출되므로,
# 실제 초기화는 한 번만 수행되도록 주의해야 합니다.
if not hasattr(self, '_initialized'): # 초기화 플래그를 사용하여 한 번만 초기화되도록
self.value = "기본값"
self._initialized = True
print("싱글톤 객체를 초기화했습니다.")
def do_something(self):
print(f"싱글톤 객체에서 작업 수행 중입니다. 현재 값: {self.value}")
# 사용 예시
print("--- 첫 번째 객체 생성 시도 ---")
s1 = Singleton()
s1.value = "첫 번째 설정값"
s1.do_something()
print("\n--- 두 번째 객체 생성 시도 ---")
s2 = Singleton()
s2.do_something()
print("\n--- 세 번째 객체 생성 시도 ---")
s3 = Singleton()
s3.value = "세 번째 설정값 (s1, s2와 동일한 객체)"
s3.do_something()
print("\n--- 객체 동일성 확인 ---")
print(f"s1과 s2는 같은 객체인가요? {s1 is s2}")
print(f"s2와 s3는 같은 객체인가요? {s2 is s3}")
print(f"s1.value: {s1.value}, s2.value: {s2.value}, s3.value: {s3.value}")
코드 설명:
_instance
= None: 클래스 변수로, 싱글톤 인스턴스를 저장합니다. 초기에는 None으로 설정합니다.__new__(cls)
:if cls._instance is None
:: 만약 _instance
가 아직 생성되지 않았다면 (즉, 첫 번째 객체 생성 시도라면)cls._instance
= super().__new__(cls)
: 부모 클래스의 __new__
메서드를 호출하여 실제 객체를 생성하고 _instance
에 저장합니다.else
: 이미 _instance가 존재하면 기존의 인스턴스를 반환합니다.__init__(self)
: __new__
는 객체를 생성하고 반환하지만, __init__
은 객체가 반환된 후 매번 호출됩니다. 따라서 _initialized
와 같은 플래그를 사용하여 실제 초기화 로직은 한 번만 실행되도록 하는 것이 중요합니다.장점
단점