본문 바로가기

Spring Study

[스프링 핵심 원리] - 기본편 #5

섹션 6 : 싱글톤 컨테이너

스프링은 대부분 웹 애플리케이션을 주로 만듬

웹 애플리케이션의 특징 - 고객 요청이 매우 많음

그런데, 스프링 없는 순수한 DI 컨테이너는 호출할 때마다 다른 객체를 생성한다.

 

하단의 테스트 결과를 봐면

memberService1 = hello.core.member.MemberServiceImpl@62bd765
memberService2 = hello.core.member.MemberServiceImpl@23a5fd2 로 서로 다른 객체가 생성되는 것을 확인할 수 있다.

만약 고객 트래픽이 초당 100이 나오면 초당 100개의 객체가 생성되고 소멸되므로 메모리 낭비가 심각해진다. 

 

이를 해결하기 위한 방안이 싱글톤이다.

해당 객체가 딱 하나만 생성되고, 이를 공유하도록 설계한다. => 싱글톤 패턴

 

싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.
  • 그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다. 
    • private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용해 인스턴스를 생성하지 못하도록 막아야 한다. 

private으로 싱글톤 생성이 되지 않음.

Assertions.assertThat(singletonService1).isSameAs(singletonService2);
// Same은 두 객체가 같은 인스턴스인지 확인한다. 두 객체가 동일한 객체인지 확인할 때 사용한다.

Assertions.assertThat(singletonService1).isEqualTo(singletonService2);
// equal은 두 객체의 내용이 같은지 비교한다. 두 객체의 속성 값이 같은지 확인할 때 사용한다.

스프링 컨테이너는 사용하면 스프링 컨테이너가 기본적으로 객체를 싱글톤으로 만들어서 관리한다. 

싱글톤 컨테이너를 사용하면 고객 요청이 100개를 오더라도 객체를 새로 생성하는 것이 아니라 있는 객체를 사용(재활용)한다. -> 성능향상

 

 

테스트 결과를 보면 동일한 객체가 사용되었음을 알 수 있다.

싱글톤 패턴의 문제점

  • 싱글톤 패턴을 구현하는 코드 자체가 많이 들어가서 복잡해진다.
  • 의존관계상 클라이언트가 구체 클레스에 의존한다. -> DIP 원칙 위반
  • 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다.
  • 유연하게 테스트하기 어렵고, 내부 속성을 변경하거나 초기화하기 어렵다. 
  • private 생성자로 자식 클래스를 만들기 어렵다.
  • 안티패턴으로 불리기도 한다. 

스프링 프레임워크는 싱글톤이 가진 문제점을 해결해서 단점은 다 제거하면서, 객체를 효율적으로 싱글톤으로 관리해준다.

스프링 컨테이너는 싱글톤 컨테이너 역할을 한다.

 

스프링 컨테이너의 역할

스프링 컨테이너는 처음 등록한 빈을 호출할 때마다 동일한 인스턴스를 반환합니다. 이 덕분에 고객의 요청이 들어올 때마다 객체를 새로 생성하는 것이 아니라, 이미 만들어진 객체를 공유하여 효율적으로 재사용할 수 있습니다.

 

싱글톤 방식의 문제점

객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다. 

 

무상태로 설계해야 한다. 

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

@Configuration과 @Bean이 모두 붙어있을 경우

@Configuration을 붙이면 바이트코드를 조작하는 CGLIB 기술을 사용해서 싱글톤을 보장한다. 

  • 스프링 컨테이너에 등록되어 있는 경우 스프링 컨테이너에서 찾아서 반환하고
  • 스프링 컨테이너에 등록되어 있지 않은 경우 기존 로직을 호출해서 생성하고 스프링 컨테이너에 등록
  • @Bean이 붙어있는 메서드마다 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 실행되어 싱글톤이 보장된다.

@Configuration을 적용하지 않고 @Bean만 적용하는 경우

  • CGLIB 기술을 사용하지 않고, 스프링 빈 등록도 되지만 싱글톤이 보장되지는 않음.