기록하는 습관

스프링 핵심 원리 - 기본편 (4) - 싱글톤 컨테이너 본문

개발/Spring

스프링 핵심 원리 - 기본편 (4) - 싱글톤 컨테이너

로그뉴 2021. 6. 27. 16:59

[ 싱글톤(Singleton) 패턴 ]

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

객체 인스턴스를 2개 이상 생성하지 못하도록 private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 한다.

 

싱글톤 미적용 상태

스프링 없는 순수한 DI 컨테이너인 AppConfig는 요청을 할 때 마다 객체를 새로 생성한다.

만약, 고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸된다.. -> 메모리 낭비가 심함!

 

싱글톤 적용 상태

 

이 객체 인스턴스가 필요하면 오직 getInstance() 메서드를 통해서만 조회 할 수 있다. 

이 메서드를 호출하면 항상 같은 인스턴스를 반환한다.

 

[ 싱글톤 패턴 문제점 ]

 

  • 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
  • 의존관계상 클라이언트가 구체 클래스에 의존한다.
    • static new 사용해 구체를 직접 생성하므로 DIP를 위반한다. 
  • 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다. 
  • 테스트하기 어렵다.
  • 내부 속성을 변경하거나 초기화 하기 어렵다.
  • private 생성자로 자식 클래스를 만들기 어렵다.
  • 결론적으로 유연성이 떨어진다.
  • 안티패턴으로 불리기도 한다.

 

[ 싱글톤 컨테이너 ]

스프링 컨테이너는 default로 객체 인스턴스를 싱글톤으로 관리한다.

스프링 컨테이너는 싱글톤 컨테이너 역할을 한다. 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라 한다.

장점

  • 싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 된다.
  • DIP, OCP, 테스트, private 생성자로 부터 자유롭게 싱글톤을 사용할 수 있다.

테스트 코드

 

싱글톤 적용 후

 

[ 싱글톤 방식의 주의점 ]

빈은 무상태(Stateless)로 설계해야 한다!!!

  • 특정 클라이언트에 의존적인 필드가 있으면 안된다.
  • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
  • 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.

Stateless 위반 예시

StatefulService의 price필드는 공유되는 필드인데, 특정 클라이언트가 값을 변경한다.

공유 필드는 정말정말 조심해야 한다!!! 스프링 빈은 항상! 언제나! 무상태(Stateless)로 설계하자.

 

[ @Configuration과 싱글톤, 바이트코드 조작의 마법 ]

결론

@Bean만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하진 않는다.

@Configuration 어노테이션이 있어야 싱글톤이 적용된다. 내부적으로 CGLIB을 사용하기 때문이다.

 

왜?

CGLIB 이라는 바이트 조작 라이브러리를 사용해 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것!

@Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성 해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.

 

 

Comments