본문 바로가기

Design Pattern

싱글톤 패턴

싱글톤 패턴

시스템 런타임, 환경 세팅에 대한 정보 등, 인스턴스가 여러개 일 때 문제가 생길 수 있으므로 인스턴스를 오직 한개만 만들어 제공하는 디자인 패턴이다. 예시로 초당 100만번 호출되는 객체가 있다고 가정할 때 100만번의 객체가 생성된다면 부하가 상당할 것이다. 그럴 때 사용하면 메모리를 효율적으로 사용할 수 있다.

대표적인 싱글톤 패턴 구현

싱글톤 패턴을 구현할 때는 이른 초기화(Eager Initialization) 방식과 늦은 초기화(Lazy Initialization) 방식이 있으며 synchronized, volatile 키워드를 사용하여 이중 체크하는 구현 방법, Enum 클래스로 구현하는 방법 등 여러가지 방법이 있지만 대표적인 방법으로는 내부 클래스(nner class)를 구현하는 방법이 있다. 

 

Initialization On Demand Holder idiom pattern

public class Singleton {
    private Singleton(){}

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

 

우선 내부 클래스를 통해 싱글톤을 구현하면 늦은 초기화로 애플리케이션 로딩 시 해당 인스턴스가 만들어지지 않으며, getInstance()를 처음으로 호출(해당 인스턴스를 처음 사용하는 시점) 했을 때 인스턴스가 만들어지게 된다.

 

다양한 방법으로 싱글톤을 구현할 수 있지만 위와 같이 내부 클래스를 이용하여 싱글톤을 구현하면 애플리케이션 로딩 시 사용되는 비용을 낮출 수 있으며, 멀티 쓰레드 환경에서도 보다 안정적으로 사용할 수 있게 된다.

 

다만 싱글톤 패턴은 대표적인 안티패턴으로 불린다. 그 이유는 하나의 같은 객체 인스턴스를 공유하기 때문에 필드가 있으면 안되는 제약이 있으며 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다. 그리고 생성자를 private 으로 막아두어 상속이 불가능하다. 즉 객체지향적의 장점인 다형성을 적용할 수 없게 된다. 또한, 싱글톤은 테스트하기가 힘드며 테스트 방법에 따라 불가능할 수 있다. 싱글톤은 생성 방식이 제한적이기 때문에 Mock 객체로 대체하기가 어려우며, 동적으로 객체를 주입하기도 힘들다. 테스트는 개발의 핵심인데, 테스트 코드를 작성할 수 없다는 것은 큰 단점이 된다.

스프링의 싱글톤 방식

스프링은 스프링 컨테이너를 만들어 객체를 싱글톤 형태로 관리한다. 이것을 싱글톤 레지스트리라고 부르며 싱글톤의 단점을 보완하였다. 우선 스프링의 싱글톤 방식은 일반적인 객체를 사용하기 때문에 private 으로 생성자를 만들 필요가 없다. 따라서 상속도 가능해지고 보다 객체지향적으로 코드를 작성할 수 있게 되었다. 그리고 테스트도 자유롭게 할 수 있게 되었다.