스프링기본8_싱글톤컨테이너

하도야지·2021년 7월 9일
0

Spring

목록 보기
8/12
post-thumbnail

싱글톤 컨테이너

(1) 웹 애플리케이션과 싱글톤

  • 스프링은 태생이 기업용 온라인 서비스 기술을 지원하기 위해 탄생함

  • 대부분의 스프링 애플리케이션은 웹 애플리케이션이다. 물론 웹이 아닌 애플리케이션 개발도 얼마든지 개발 할 수 있음.

  • 웹 애플리케이션은 보통 여러 고객이 동시에 요청을 한다.

    -> 고객이 3번 요청하면 객체가 3개 생성됨
    --> 요청이 올 때 마다 계속 객체를 만들어야 함

  • 스프링 없는 순수한 DI 컨테이너

    -> 순수한 DI컨테이너 AppConfig는 요청할 때 마다 객체를 매번 생성한다.
    -> 고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸된다 > 메모리 낭비가 심하다.
    -> 해결방안은 해당 객체가 딱 1개 생성되고 공유하도록 설계하면됨 ( = 싱글톤 패턴)


(2) 싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
  • 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.
    -> private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 한다.
  1. static 영역에 객체 instance를 미리 하나 생성해서 올려둔다.
  2. 이 객체 인스턴스가 필요하면 오직 'getInstance()'메서드를 통해서만 조회할 수 있다. 이 메서드를 호출하면 항상 같은 인스턴스를 반환한다.
  3. 딱 1개의 객체인스턴스만 존재해야 하므로, 생성자를 private으로 막아서 혹시라도 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막는다.
  • Test확인

    -> private으로 new 키워드를 막아둠.
    --> 호출할 때 마다 같은 객체 인스턴스를 반환하는 것을 확인할 수 있다.

[싱글톤 패턴의 문제점]

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

(3) 싱글톤 컨테이너

- 스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 생글톤(1개만 생성)으로 관리한다.
- 지금까지 우리가 학습한 스프링 빈이 바로 싱글톤으로 관리되는 빈이다.

  • 스프링 컨테이너는 싱글턴 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
    -> 컨테이너 생성과정을 보면 객체를 하나만 생성해서 관리함
  • 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다.
    -> 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라 한다.
  • 스프링 컨테이너의 이런 기능 덕분에 싱글턴 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지할 수 있다.
    ->싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 된다.
    ->DIP, OCP, 테스트, private 생성자로부터 자유롭게 생글톤을 사용할 수 있다.
  • 스프링 컨테이너 적용 테스트


    ->스프링 컨테이너 덕분에 고객의 요청이 올 떄마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 재사용할 수 있다.(싱글톤 방식 외에 다른 방법도 지원함)

(4) 싱글톤 방식의 주의점

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

  • 무상태(stateless)로 설계해야 한다!
    ->특정 클라이언트에 의존적인 필드가 있으면 안됨
    ->특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안됨
    -> 가급적 읽기만 가능해야 한다.
    -> 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, threadLocal 등을 사용해야 한다.
    -> 스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있다.

  • 상태공유 예시와 테스트


    -> ThreadA가 사용자A코드를 호출하고, ThreadB가 사용자 B코드를 호출한다고 할 떄
    --> 'StatefulService'의 price필드는 공유되는 필드인데, 특정 클라이언트가 값을 변경한다.
    ---> 사용자A의 주문금액은 10000원이 되어야 하는데, 20000원 이라는 결과가 나왔다.
    ----> 공유 필드는 조심해야함. 스프링 빈은 항상 무상태(stateless)로 설계하자.

  • 상태 공유 부분 올바른 설계


(5) @Configuration과 싱글톤

  • 문제점?
  1. memberService 빈을 만드는 코드를 보면 'memberRepository()를 호출한다.
    --> 이 메서드를 호출하면 'new MemoryMemberRepository()'를 호출한다.
  2. oderService 빈을 만드는 코드도 동일하게 'memberRepository()를 호출한다.
    --> 이 메서드를 호출하면 'new MemoryMemberRepository()'를 호출한다.

---> 결과적으로 각각 다른 2개의 MemoryMemberREpository가 생성되면서 싱글톤이 깨지는 것 처럼 보인다. 스프링 컨테이너는 이 문제를 어떻게 해결할까?

  • 테스트
    -> MemberServiceImpl/ OrderServiceImpl 에 테스트용도 코드 생성


    ->테스트 시 인스턴스가 동일함 확인 가능

(6) @Configuration과 바이트코드 조작의 마법

스프링 컨테이너는 싱글톤 레지스트리다.
따라서 프르이 빈이 싱글톤이 되도록 보장해주어야한다.
그런데 스프링이 자바 코드 까지 어떻게 해결할 수는 없다.
그래서 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사용한다.
@Configuration을 적용한 AppConfig에 비밀이 있다.


-> 순수한 클래스면 class springboot.core.Appconig 라고 출력되어야함.
--> xxxCGLIB가 붙음
---> CGLIB 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것

  • 스프링 컨테이너에는 AppConfig@CGLIB가 들어가 있음
    -> 이 녀석이 싱글톤을 보장해줌
  • @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어 진다.
    -> 덕분에 싱글톤 보장

@Configuration 없이 @Bean만 적용하면 어떻게 될까?

  • 순수한 AppConfig로 스프링 빈에 등록 됨
  • MemberRepository가 3번 호출됨 -> 싱글톤 깨짐
profile
프로그래머를 꿈꾸는 코더

0개의 댓글

관련 채용 정보