싱글톤 패턴

홍준식·2024년 6월 16일

어플리케이션이 실행되는 동안 클래스가 단 하나의 유일한 인스턴스를 가지도록 하는 패턴

싱글톤은 DB 연결 모듈, 스레드 풀 등과 같이 리소스를 많이 사용하는 무거운 클래스를 매번 생성하지 않고 하나의 인스턴스만을 통해 동작하여 메모리를 절약하고 해당 인스턴스가 단 하나만 존재하는 것을 보장하기 위한 패턴이다.

대표적으로 스프링에서는 기본값으로 빈 오브젝트를 모두 싱글톤으로 만드는데 대규모 요청을 처리하는 환경에서 요청이 올 때마다 새로운 오브젝트를 생성하는 것은 서버에 큰 부하를 생성하고 싱글톤을 사용하여 서버의 부하를 줄이고 있다.

싱글톤 패턴 문제점

의존성 증가

싱글톤 패턴을 사용하면 하나의 싱글톤을 여러 모듈들이 공유하게 되고, 싱글톤 인스턴스가 변경되면 참조하는 모든 모듈들이 수정되는 문제가 존재한다.

테스트의 어려움

싱글톤 패턴의 인스턴스는 자원을 공유하고 있다. 이는 서로 독립적으로 동작하는 단위 테스트에 어려움을 준다.

SOLID 위반

싱글톤 인스턴스를 하나만 생성하기 때문에 여러가지 책임을 지니는 경우가 많아지고, 이는 단일 책임 원칙을 위반하는 결과를 발생시키기도 한다. 또한, 여러 클래스에서 사용하여 클래스간 결합도가 높아져 개방-폐쇄 원칙을 위반하기도 한다.
이때문에 싱글톤 패턴을 객체 지향 프로그래밍의 안티 패턴이라고 부르기도 하며 싱글톤이 하나의 책임만을 가지고 오남하지 않도록 해야 한다.

예제

JAVA

public class Singleton {
    private static Singleton singletonObject;

    private Singleton() {
    }
    
    public static Singleton getInstance() {
        if (singletonObject == null) {
            singletonObject = new Singleton();
        }
        
        return singletonObject;
    }
}
public class Client {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        System.out.println(instance1 == instance2);  // true
    }
}

위와 같이 싱글톤을 구현할 수 있다.
하지만, 멀티 스레드 환경에서는 if (singletonObject == null) 구문이 실행될 때에 다른 스레드에서 인스턴스를 생성하고, 여러 인스턴스가 생성될 수 있는 문제가 존재한다.
즉, thread safe하지 않다.

이를 해결하기 위해서는 지연 로딩을 사용하거나, Enum 사용을 권장한다.
자바의 Enum은 모든 클래스가 싱글톤으로 동작하도록 설계되어있어 가장 간단하게 멀티 스레드환경의 싱글톤을 구현할 수 있다.

public enum Singleton {
	INSTANCE;

	public void doSomething() {
		// do something
	}
}
public class Client {
	public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE();
        singleton.doSomething();
    }
}

Python

class Singleton:
	_instances = {}
	
	def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]


if __name__ == "__main__":
    s1 = Singleton()
    s2 = Singleton()

    print(id(s1) == id(s2))

python에서는 다음과 같이 싱글톤 패턴을 구현할 수 있다.

0개의 댓글