Spring

[Spring] 싱글톤 패턴이란

뚜코맨 2024. 1. 5. 18:33

 

김영한 스프링 강의를 듣던 중 싱글톤패턴의 개념이 나왔다.

 

싱글톤 패턴이란 객체(인스턴스)가 현재 JVM 안에 단 하나만 존재해야한다라는 것이다.

싱글톤패턴이 왜 생겼는가?

 

스프링의 탄생부터 알아보자

  • 스프링은 기업용 온라인 서비스 기술을 지원하기 위해 탄생했다.
  • 대부분의 스프링 애플리케이션은 웹 애플리케이션이고, 물론 웹이 아닌 애플리케이션도 얼마든지 개발할 수 있다.
  • 웹 애플리케이션은 보통 여러 고객이 동시에 요청을 한다.

그림을 통해 예를 들어보자

 

우리가 만들어둔 AppConfig(DI 컨테이너)가 있다고 가정하자.

 

클라이언트 A,B,C가 스프링한테 memberService를 요청하면,

DI 컨테이너 안에서 요청 건수마다 new를 통해 객체를 생성해서 반환하게 된다.

 

웹 애플리케이션 특성 상 고객이 많은 요청을 받게 되고 요청이 올때마다 객체를 생성하게 되는데...

만약! 동일한 요청들을 전부 상이한 메모리 공간에 할당시켜 각각 응답해주게 되면 메모리 공간이 남아나질 않을 것이다.

 

코드를 통해 정말 객체가 새로 생성되었는지 알아보자

 

soutv로 값을 찍어보면 같은 memberService지만 참조값은 다른 값이 들어오게 된다. 즉, 다른 인스턴스를 갖고 있다는 것이다.

이렇듯 요청이 n개 들어오면 n번의 메모리 할당이 된다는 것을 확인할 수 있었다.

 

이걸 해결하기 위해 싱글톤패턴을 적용하면 어떻게 될까? 그림을 통해 알아보자.

몇개의 요청이던, 단 1개의 동일한 memberService를 반환시킬수 있다.

 

싱글톤패턴을 어떻게 구현하는지 코드를 통해 알아보자.

 

이런 구조로 개발을 하면 싱글톤패턴을 완벽하게 구현해낼 수 있다.

  • static 영역에 객체 instance를 미리 하나 생성해서 올려둔다.
  • 해당 객체 인스턴스가 필요하면 오직 getInstance() 메서드를 통해서만 접근 할 수 있다. 해당 메서드를 호출하면 항상 같은 인스턴스를 반환한다.
  • 생성자를 private로 막아서 instance가 여러 개 생성 되는 것을 막는다.

테스트 코드를 통해 과연 정말 같은 인스턴스가 맞는지 확인해보자.

 

JUnit의 isSameAs() 메서드를 사용해서 과연 내가 생성한 singletonService1, 2가 과연 같은 인스턴스를 갖고 있는가를 검증해보자.

 

이렇게 참조값이 같은걸 확인 할 수 있다.

 

싱글톤 패턴을 적용하면 고객의 요청에 마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 사용이 가능하다! 이렇게만 보면 단점이 없어 보이지 않는가 하지만 싱글톤패턴은 다음과 같은 단점들을 갖고 있다.

 

싱글톤 패턴 단점

  1. 싱글톤 패턴 자체를 구현하는 코드가 많이 들어간다.
  2. 의존관계상 클라이언트가 구체 클래스에 의존한다. -> DIP 위반
  3. 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다
  4. private 생성자로 자식 클래스를 만들기 어렵다.
  5. 유연성이 떨어진다.

과연 그럼 이런 좋은 싱글톤 패턴을 이러한 단점들 때문에 사용을 못하는 것인가? No!

 

스프링에서는 이러한 싱글톤 패턴의 장점들을 살리고 문제점을 해결해서 스프링 컨테이너를 제공한다!

 

싱글톤 컨테이너 = 스프링 컨테이너

  • 스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
  • 바로 스프링 빈이 싱글톤으로 관리되는 빈이다.
  • 스프링 컨테이너는 이런 기능 덕분에 싱글턴 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지할 수 있다.
    • 싱글톤 패턴을 위한 지저분한 코드가 필요가 없다.
    • DIP, OCP, 테스트, private 생성자로 부터 자유롭게 싱글톤을 사용할 수 있다.

그럼 스프링 컨테이너를 사용해서 객체를 호출 해보자.

 

단순히 new AppConfig 생성해서 객체를 가져오는 것이 아니라 ApplicationContext 즉, 스프링 컨테이너를 생성하고 스프링 컨테이너에 등록되어 있는 bean(객체)를 가져와서 soutv를 찍어보고 과연 memberService1, 2는 같은지 isSameAs() 테스트를 해보자.

 

 

memberService1, 2는 모두 같은 memberServiceImpl객체를 같은 메모리 값으로 참조하고 있는걸 확인할 수 있다.

 

다시 한번 스프링 설계에 감탄을 하고 간다....