싱글톤 패턴(Singleton pattern)

2023. 11. 21. 02:00DesignPattern

싱글톤패턴과 귀여운 캐릭터를 그려달라 요청한 DALL-E  생성 이미지

 

싱글톤 패턴이란, 특정 클래스가 프로그램 전체에서 오직 하나만 생성되도록 보장하는 패턴입니다.

또한 전역적으로 하나의 인스턴스를 유지하며, 리소스의 중복사용을 방지하고, 전역적인 접근 지점을 제공합니다.

 

 

싱글톤패턴을 사용하기 위해선 몇 가지 사항을 고려해야 합니다.

  • 스레드 안전성: 멀티스레드 환경에서 싱글톤 인스턴스의 생성과 접근은 스레드 안전하게 관리되어야 합니다.
  • 예외 처리: 초기화 과정에서 발생할 수 있는 예외 사항들을 적절히 처리해야 합니다.
  • 자원 관리: 싱글톤으로 생성된 인스턴스가 사용하는 자원(파일 핸들, 네트워크 연결 등)은 적절히 관리하고 해제해야 합니다.

프로그램을 작성하다 보면 하나만 가지고 있어도 괜찮은 요소들이 있어서 싱글톤 패턴을 활용하여 리소스를 크게 줄일 수 있습니다.

 

아래 예시코드는 사이드 프로젝트 코드의 일부이고 싱글톤패턴을 활용하고 공부해 보게 되는 고민으로 이어진 코드입니다.

- 기존 로스트아크 OPEN API 통신연결을 위한 클래스

class LoaGateWay:
    def __init__(self) -> None:
        load_dotenv()
        self.base_url = "https://developer-lostark.game.onstove.com/"
        self.session = requests.Session()
        headers = {
            "accept": "application/json",
            "authorization": f"bearer {os.environ.get('LOA-API-KEY')}",
        }
        self.session.headers.update(headers)

 

위 코드에서 생성되는 요소들은 여러 번 생성될 필요가 없는 요소이고 변화가 없다시피 하는 요소입니다.

따라서 연결에 필요한 공통요소와 세션을 부모 클래스로 생성해 주고,
각 기능별 API를 호출하고 처리하는 로직이 들어가는 자식 클래스들을 만들어주는 설계를 생각했습니다.

- 싱글톤 패턴을 적용하여 리팩터링 한 LoaGateWay 클래스
class LoaGateWay:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(LoaGateWay, cls).__new__(cls)
            cls._instance.__init__(*args, **kwargs)
        return cls._instance

    def __init__(self) -> None:
        if not hasattr(self, "_initialized"):
            load_dotenv()
            self.base_url = "https://developer-lostark.game.onstove.com/"
            self.session = requests.Session()
            headers = {
                "accept": "application/json",
                "authorization": f"bearer {os.environ.get('LOA-API-KEY')}",
            }
            self.session.headers.update(headers)

            self._initialized = True

위 코드를 보면 __new__()에서 클래스를 호출하여 인스턴스를 만들 때,
_instance 가 있는지 없는지를 판단하여 없으면 해당 인스턴스를 만들어주고 [cls._instance.__init__(*args, **kwargs)], 있다면 기존에 생성된 인스턴스를 리턴해줍니다.

또한 이미 호출되어 인스턴스가 생성된 상태에서 추가적으로 인스턴스를 호출할 때 초기화__init__()에서
_initialized True/False값을 통하여 초기화의 중복을 방지합니다.


디자인 패턴에 대한 첫 게시물이고 , 처음으로 접한 패턴이라 마냥 좋은 줄만 알았는데

 

대부분 싱글톤패턴을 사용하면 10중 9는 프로그램 디자인 기본원칙을 위배한다고 한다.

 

그래서 싱글톤 패턴의 대안 패턴이 어떤 것이 있는지 간단하게 알아보고 , 추후에 다뤄볼 기회가 있다면 다뤄볼 생각이다.

 

- 서비스 로케이터 패턴
서비스와 의존성을 중앙 레지스트리에 등록하고, 필요할 때 이를 조회하여 사용합니다. 이는 의존성 관리를 유연하게 만듭니다.

 

- 의존성 주입 패턴

객체의 의존성을 외부에서 주입함으로써, 객체 간의 결합도를 낮추고 유연성 및 테스트 용이성을 증가시킵니다.

 

- 팩토리 메서드 패턴

객체 생성을 서브클래스에 위임하여, 클라이언트 코드가 특정 클래스에 직접 의존하지 않도록 합니다. 이를 통해 생성 과정의 캡슐화와 유연성을 제공합니다.

 

- 레지스트리 패턴

전역 레지스트리를 통해 객체 인스턴스를 관리하고, 필요할 때 해당 객체에 접근할 수 있도록 합니다. 이는 여러 객체 인스턴스의 관리를 용이하게 합니다.