🧩 싱글톤 패턴 정리

싱글톤 패턴은 특정 클래스의 인스턴스가 오직 하나만 생성되는 것을 보장하는 디자인 패턴입니다.
객체를 여러 개 생성하여 발생하는 메모리 낭비를 방지하고, 자원을 효율적으로 공유할 수 있습니다.


🔧 싱글톤 패턴 구현 방식

  • private 생성자를 통해 외부 인스턴스 생성을 제한
  • 클래스 로딩 시점에 static으로 유일한 인스턴스를 생성
  • public static getInstance() 메서드를 통해 외부에서 접근 가능
public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}

❌ 싱글톤 패턴의 단점

잘못 사용하면 안티패턴이 될 수 있습니다.

  • DIP 위반
    → 클라이언트가 인터페이스가 아닌 구체 클래스에 의존
Service service = SingletonService.getInstance(); // 인터페이스 X
  • OCP 위반 가능성
    → 변경에 유연하지 않음
public class DiscountService {
    public int calculatePrice(int price) {
        return SingletonDiscountPolicy.getInstance().discount(price);
    }
}
  • 테스트 어려움
    → 목(Mock) 객체로 대체 어려움

  • 내부 상태 초기화 어려움
    → 테스트 간 영향 발생

SingletonLogger logger = SingletonLogger.getInstance();
logger.setOutputFile("log1.txt"); // 테스트마다 변경 곤란
  • 상속 불가
    → 클래스 확장이 필요할 경우 문제 발생
public class MySingleton extends Singleton { // 에러
}
  • 멀티스레드 환경에서 위험
    → 상태를 가지면 심각한 장애 발생 가능
public class StatefulService {
    private int price;
    public void order(String user, int price) {
        this.price = price;
    }
    public int getPrice() {
        return price;
    }
}

🌱 스프링의 싱글톤 컨테이너

스프링은 객체를 자동으로 싱글톤으로 관리합니다.

  • @Component, @Service, @Repository 등으로 등록된 빈은 기본 싱글톤 스코프
  • 의존성 주입(DI)을 통해 DIP, OCP 원칙 준수
  • Mock 객체 대체가 쉬움 → 테스트에 유리
  • 싱글톤 레지스트리를 통해 스프링 컨테이너가 직접 관리

⚠ 싱글톤 빈 사용 시 주의사항

하나의 객체를 여러 요청이 공유 → 상태를 가지면 안 됨

  • 반드시 무상태(stateless)로 설계할 것
  • 사용자 정보 등 요청별 데이터는 지역 변수/파라미터/ThreadLocal 사용
  • 빈의 필드에 공유 상태 저장 절대 금지 ❌

🔍 @Configuration의 역할

  • @Configuration 클래스는 CGLIB 바이트코드로 프록시를 만들어 싱글톤 보장
  • @Bean 메서드 여러 번 호출해도 항상 같은 객체 반환
  • @Configuration 없이 @Bean만 사용하면 매번 새로운 인스턴스 생성될 수 있음 → 주의!

🧾 요약

  • 싱글톤 패턴은 객체 생성을 하나로 제한하는 유용한 방식이지만, 다양한 단점과 제약 존재
  • 스프링은 이를 해결하기 위해 싱글톤 컨테이너 + 의존성 주입 + 무상태 설계를 제공
  • 직접 구현보다 스프링 빈을 통한 싱글톤 활용이 더 바람직하고 권장됨