본문 바로가기

SERVER/Spring

[spring-core-3] 싱글톤 컨테이너

인프런 [스프링 핵심 원리 - 기본편]을 정리한 내용입니다.

싱글톤 컨테이너

문제 정의: 기존의 스프링이 없는 순수한 DI 컨테이너인 AppConfig는 요청을 할 때마다 객체를 새로 생성해도 반환해줬다.

이 경우 트래픽이 클 수록 많은 객체가 생성되고 소멸되며 메모리 낭비가 심한 문제가 있고, 따라서 해결하기 위해 싱글톤 패턴을 사용한다.

 

싱글톤 패턴

클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴.

 

//1. static 영역에 객체를 딱 1개만 생성해둔다.
private static final SingletonService instance = new SingletonService();

//2. public으로 열어서 객체 인스턴스가 필요하면 이 static 메소드를 통해서만 조회하도록 허용한다.
public static SingletonService getInstance(){
    return instance;
}

//3. 생성자를 private으로 선언해서 외부에서 new 키워드를 사용한 객체 생성을 못하게 막는다.
private SingletonService(){
}

 

싱글톤 패턴의 문제점

  • 싱글톤 패턴을 구현하는 코드가 많이 필요
  • 클라이언트가 구체 클래스에 의존 -> DIP 위반 (구체클래스인 SingletonService.getInstance()로 객체를 꺼내야 한다.), OCP 원칙을 위반할 가능성이 높다.
  • 테스트가 어렵다.
  • 내부 속성을 변경하거나 초기화하기 어렵다.
  • private 생성자 -> 자식 클래스를 만들기 어렵다.

싱글톤 컨테이너

  • 스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도 객체 인스턴스를 싱글톤으로 관리한다. -> 싱글톤 패턴의 문제점을 해결(싱글톤 패턴을 위한 코드 불필요, DIP, OCP, private 생성자, 테스트 관련 문제 해결)하면서 객체를 싱글톤으로 유지할 수 있게 해준다!
  • 스프링 컨테이너는 싱글톤 컨테이너 역할을 하며, 이러한 기능을 싱글톤 레지스트리라고 한다.

싱글톤 방식의 주의점

  • 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하는 것이기 때문에 싱글톤 객체는 **무상태(stateless)**로 설계해야 한다.
  • 무상태(stateless)
    • 특정 클라이언트에 의존적인 필드가 있으면 안됨.
    • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안됨.
    • 가급적 읽기만 가능해야 함.
    • 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 함.

📌 스프링 빈은 항상 무상태로 설계하자!

 

@Configuration과 싱글톤

 

기존의 AppConfig 코드 일부를 다시 보자.

 

@Bean
public MemberService memberService(){
    return new MemberServiceImpl(memberRepository());
}

@Bean
public MemberRepository memberRepository() {
    return new MemoryMemberRepository();
}

@Bean
public OrderService orderService(){
    return new OrderServiceImpl(memberRepository(), discountPolicy());
}

 

MemberService 빈을 만드는데 memberRepository()를 호출 & OrderService 빈을 만드는데 memberRepository()를 호출. -> 모두 new MemoryMemberRepository()가 호출될 것임.

 

 두 번의 MemoryMemberRepository를 호출하는 것으로 보인다. 싱글톤이 깨지는걸까?

 확인해보면, memberRepository 인스턴스는 모두 같은 인스턴스가 공유되어 사용된다. 실제로 빈으로 등록될 때 로그를 남겨보면 memberRepository는 한번밖에 호출되지 않는 것을 확인할 수 있다.

 

@Configuration과 바이트코드 조작

- AnnotationConfigApplicationContext에 파라미터로 넘긴 값을 스프링 빈으로 등록되므로 AppConfig도 스프링 빈으로 등록되는데, 이 스프링 빈을 조회해서 클래스 정보를 출력해보면 xxxCGLIB가 붙은 것을 확인할 수 있다.

- 이는 스프링이 CGLIB라는 바이트 코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 클래스를 스프링 빈으로 등록했기 때문이다. 이 클래스가 싱글톤이 보장되도록 해준다.

- 이 클래스는 @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 없으면 생성해서 등록 후 반환하는 로직을 가져 싱글톤을 보장해준다.

- 이는 @Configuration이 적용되었기 때문에 가능한 것으로, 이 애노테이션을 붙이지 않은 AppConfig를 등록하면 스프링 빈으로 등록은 되지만, 싱글톤을 보장하지 않는다.