💡 Intro

  • 왜 하필 스프링 인가? 라는 질문을 던지게 되면서 스프링이 가지고 있느 장점에 대해서 고민해보았다.
  • 그 중 기존의 싱글톤 패턴의 한계를 뛰어넘은 스프링 싱글톤 레지스트리를 알게 되었다. 이전에 단순히 스프링이 싱글톤 scope으로 객체를 관리하여 여러 요청이 동시에 들어오는 환경에서 안정적으로 서비스할 수 있다는 장점을 들었지만 싱글톤 패턴과 어떻게 다른지 알지 못했다.
  • 그래서 알아보자 🙌

🌩 싱글톤 패턴이란?

왜 싱글톤 패턴이어야 할까

  • 하나의 서버에 초당 수많은 요청이 도착하고 처리되어야 한다. 요청이 올 때마다 서버에서 관련 객체를 생성하는 것은 굉장히 큰 오버헤드이다.
    • 어떤 오버헤드가 발생할 수 있을까 ?

      주로 서버는 여러 계층의 레이어를 지나고 여러 비지니스 오브젝트들이 협력하여 요청을 처리한다.

      요청 하나당 대충 5개 정도의 오브젝트가 생성되고, 초딩 300개의 요청, 1분에 18000개의 요청이 발생한다면 18000 * 5 만큼의 새로운 오브젝트가 생성된다.

      오브젝트 생성도 비용이지만 생성된 오브젝트를 처리하여 메모리를 관리하는 GC도 오버헤드다. (GC의 동작원리를 살펴보자 🙌)

  • 따라서 스프링은 요청을 서비스 해주는 객체를 하나만 만들고 그것을 공유할 수 있도록 한다.
  • 하나의 객체만 생성하여 공유할 수 있는 것이 싱글톤 패턴이다.

싱글톤 패턴이 가지고 있는 단점

  • private 생성자 이므로 상속이 불가능하여 객체지향의 특징을 활용할 수 없다.
  • 싱글톤은 테스트하기 어려우며 서버환경에서 동시에 여러 요청이 들어오기 때문에 하나만 만들어지는 것을 보장할 수 없다.
    • 싱글톤 객체는 만들어지는 방식이 제한적이므로 Mock 객체로 대체하는 것이 어렵다. 또한 생성자로 다이나믹하게 의존 객체를 주입하기 어렵기 때문에 일부 대체하려는 경우 해당 오브젝트를 테스트용으로 아예 만들어서 사용할 수밖에 없다.
  • 싱글톤은 전역상태를 만들기 때문에 객체지향적으로 보았을 때 바람직하지 못하다.
    • static 필드와 메서드를 사용하며 싱글톤을 static 메서드를 통해 어느 곳에서든 접근할 수 있다.

🌩 싱글톤 레지스트리

  • 스프링은 위에서 언급한 이유로 특정 서비스 객체들(빈)을 싱글톤으로 만든다. 하지만 위에서 나온 싱글톤 패턴을 그대로 사용하기보다 단점을 보완한 상태를 사용한다.
  • 싱글톤 패턴의 단점을 보완하기 위해 나온 것이 스프링의 싱글톤 레지스트리이다. 스프링 컨테이너가 싱글톤 레지스트리의 역할을 하여 빈을 싱글톤으로 관리한다.
    • private 생성자로 객체의 생성을 막는 방법이 아니라 일반 자바 클래스를 싱글톤으로 활용할 수 있도록 지원한다.
    • 생성, 관계설정, 사용에 대한 제어권이 컨테이너에게 있기 때문에 일반 자바 클래스도 싱글톤으로 관리될 수 있다.
  • public 생성자를 사용할 수 있기 때문에 필요하다면 새로운 오브젝트를 생성하고 Mock 오브젝트로 대체하는 등의 작업을 할 수 있다.
  • 객체지향적 설계와 디자인 패턴 적용이 가능하다.

🌩 싱글톤이기 때문에 주의할 점 ‼️

  • 멀티 스레드 환경에서 여러 스레드가 싱글톤에 동시접근 할 수 있기 때문에 상태 관리를 주의해야한다.
    • 대부분은 stateless하게 만들어져야 한다. 내부 상태값의 동시수정이 이루어지는 경우 매우 위험하기 때문이다.(읽기 전용은 제외)
  • 싱글톤에서 필요한 정보는 상태없이 어떻게 관리할까?
    • 파라미터, 로컬 변수, 리턴 값 등을 이용한다.
    • 위 변수들은 스택 영역에 독립적으로 저장이 되기 때문에 스레드마다 분리되어 있다.

🌩 스프링의 빈의 scope는 모두 싱글톤일까

  • 우선, scope 란?
    • scope는 범위라는 뜻의 용어이고 여기서는 스프링의 빈이 1) 언제 생성되고 2) 언제까지 존재하며 3) 어디까지 적용되는지를 말한다.
    • 기본적인 스프링 빈의 scope는 싱글톤이며 빈의 생명주기는 스프링 컨테이너와 함께한다.
  • 이외의 scope도 존재한다!
    • 프로토타입 Prototype scope
      • 컨테이너에 빈을 요청할 때마다 매번 새로운 오브젝트를 생성
    • 요청 Request scope
      • 새로운 HTTP 요청이 생길 때마다 생성
    • 세션 Session scope
      • 웹의 세션과 유사하게 생성

🛋 느낀 점

  • Spring MVC를 직접 구현해보는 미션에서 component scanning으로 빈을 등록했던 것을 떠올리며 위 개념을 학습했더니 더 와닿았다.
  • 기존의 싱글톤 패턴이 안티패턴인데 멀티 스레드 환경에서는 싱글톤을 사용해야하는 것을 스프링 컨테이너로 해결한 것이 신선했다.
  • 스프링은 프레임워크이고 프레임워크에 종속적일수록 자바의 이점을 활용하기 어렵다고 생각했는데 이제야 조금씩 토비의 스프링에서 스프링 프레임워크가 자바의 객체지향적인 장점을 잘 살릴 수 있는 프레임워크라고 한 것이 이해되는 것 같다.


[참고자료]

  • 토비의 스프링