[Spring-plus] 싱글톤에 대한 이야기(Spring-Singleton)
🫠

[Spring-plus] 싱글톤에 대한 이야기(Spring-Singleton)

1. 싱글톤이란

싱글톤에 대한 설명
싱글톤(Singleton)은 디자인 패턴 중 하나로, 어떤 클래스가 최초 한 번만 메모리를 할당하고(static) 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴이다. 이렇게 하면, 메모리 낭비를 방지할 수 있다.
 
발전 과정
  1. 스프링은 기업용 온라인 서비스 기술을 지원하기 위해 탄생하였다.
  1. 스프링은 웹 어플리케이션이다.
  1. 웹 어플리케이션은 동시성 문제가 있다. ( 동시에 여러 고객이 요청함)
 
DI 컨테이너 동작 과정
  • 고객이 요청할 때마다 객체를 새로 생성한다.
  • 웹 어플리케이션은 고객이 계속 요청을 시도한다.
  • 그렇다면 계속해서 고객이 요청한다면?
  • 지금 Spring 이전에 Appconfig는 계속해서 유저가 접근할 때마다 객체를 생성한다
 
 

2. 싱글톤 패턴

싱글톤 패턴이란
클래스의 인스턴스가 딱 하나만 생성되는 것을 보장하는 디자인 패턴이다. 그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막는다.
  • private 생성자를 사용해서 외부에서 임의로 생성하지 못하게 한다.
 
  • 해당 객체의 인스턴스가 필요하면 getInstance() 메서드를 통해서만 조회할 수 있다.
  • 딱 한개의 개발 인스턴스만 존재함으로, private으로 생성자를 사용해서 new 키워드로 객체 인스턴스가 생성되는 것을 막는다.
 
 
싱글톤 패턴에는 어떤 문제점이 있을까?
  1. 구현하는 코드가 많이 들어간다.
  1. 클라이언트가 구체 클래스에 의존한다(dip 위반)
  1. 테스트하기 어렵다
  1. 내부 속성에 대한 변경이 정말 어렵다.
 
 

3. 싱글톤 컨테이너

  • 스프링 컨테이너느 싱글톤 패턴을 적용하지 않아도 객체 인스턴스를 싱글톤으로 관리한다.
  • 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다. 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 한다.
 
  • 해당 테스트에서 getBean을 통해서 객체를 반환한다.
  • 두개의 객체를 비교하였을 때 같은지에 대한 테스트를 진행한다.
  • 스프링 컨테이너는 싱글톤이지만, 새로운 객체를 생성해서 반환하는 기능도 사용할 수는 있다.
 
 

4. 싱글톤 방식의 주의점

싱글톤 방식을 사용할 때 같은 인스턴스를 공유하기 때문에 값 접근에 있어 정말 조심해야 한다.
  • 특정 클라이언트에 의존적인 필드가 있어서는 안된다.
  • 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
  • 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터 등을 사용해야 한다.
  • 가급적 읽기만 가능하여야 한다.
 
예시
  • price라는 필드를 공유하고, 주문과 주문한 금액을 조회하는 로직이 있다.
 
  • 만약 주문 test1과 test2가 생성되었을 때
  • test1의 주문 금액을 가져오면 어떻게될까?
같은 객체로 필드를 공유하였기 때문에 20000원이 된다.
 
해결 방법
  • price 부분을 int로 넘겨주면 된다. (지역변수 사용)
 
 

5. 싱글톤 컨테이너 @Configuration

Appconfig를 의심해라
  • 해당 코드는 다음과 같이 의존성 주입을 통해서 객체를 생성한다.
  1. memberService → new MemoryMemberRepository
  1. orderService → new MemoryMemberRepository
 
💡
객체가 두번 생성되는데 싱글톤이 유지가 될 수 있을까?
 
확인해보자
  1. MemberServiceImpl.java 코드 변경
 
  1. OrderServiceImpl.java 코드 변경
 
  1. 싱글톤 테스트
  • 해당 테스트는 성공이라고 뜬다.
  • 분명 객체는 3번 생성할텐데 왜 다 같은 값이 나오는걸까?
 
 
AppConfig도 결국 Bean이다
앞서 스프링 컨테이너는 싱글톤 레지스트리라고 말했다. 하지만 아까처럼 3번 호출 하는 경우 다른 객체가 생성이 된다. 그래서 스프링은 클래스의 바이트코드를 조작하는 라이브러리가 있다.
 
한번 확인해보자
  • 이상한 Enhance 어쩌구가 나온다.
  • 스프링은 CGLIB라는 바이트코드 조작 라이브러릴 사용해서 AppConfig 클래스를 상속받은 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한다.
  • 해당 라이브러리는 스프링 Configureation에서 싱글톤을 보장해준다.
  • 해당 라이브러리는 스프링 컨테이너를 조회해서 스프링 컨테이너에 등록된 Bean이면 반환하고, 없으면 새로 생성하고 스프링 컨테이너에 등록한다.
 
 
Spring에서 Bean을 Container에 등록할 때 @Configuration을 써야 하는 이유
  1. Configuration 어노테이션을 붙이지 않아도 Bean은 등록 된다.
  1. 하지만 CGLIB 기술이 적용되어 있지 않기 때문에 싱글톤 보장이 안된다.
  1. 따라서 DI 컨테이너를 작성할 때는 꼭 Configuration 어노테이션을 사용하길 바란다.