Spring과 싱글톤 패턴

debbu·2021년 8월 15일
0

싱글톤 패턴

클라이언트가 요청을 보낼때 마다 객체의 인스턴스가 생성된다면???
tps가 과도하게 높은 요청이 보내진다면?
메모리 낭비가 어마무지하게 심할것!!!

싱글톤 패턴은 이를 방지하기위해 딱 한개의 객체만 생성하고 이를 공유하도록 설계됨.
따라서 객체 인스턴스가 두개 이상 생성되지 않도록 막아야함!

private생성자를 사용해서 외부에서 new를 쓰는것을 봉쇄해야함

간단한 싱글톤패턴 적용예시

  1. static영역에 객체 인스턴스 하나를 생성하여 미리 올려둠
  2. public으로 열어둔 static메서드 getInstance()를 추가함
  3. 이 인스턴스가 필요할 시 getInstance()를 통해서 접근함. => 항상 같은 인스턴스 참조
  4. 딱 1개만 존재해야 하므로 생성자를 private로 막아 외부에서의 new키워드 방지

다만 이런 식으로 싱글톤패턴을 적용하면 수많은 문제점들이 발생한다.
위 방식은 한 객체인스턴스를 여러 클라이언트가 공유하기 때문에 원치 않은 내부속성 변경이나,
여러 객체지향 설계를 위반할 가능성이 커진다.


스프링에서 싱글톤

  • 스프링에서는 스프링컨테이너를 활용하여 싱글톤패턴의 장점만을 살리고 있다.

싱글톤 컨테이너

  1. 스프링 컨테이너는 복잡한 코드로 싱글톤 패턴을 구현하지 않아도 자동으로 객체 인스턴스를 싱글톤으로 관리한다.
  2. 즉, 스프링 컨테이너는 싱글톤 컨테이너의 역할을 하며 이렇게 관리하는 기능을 싱글톤 레지스트리라고 한다.
  3. DIP, OCP, private생성자로부터 자유롭게 코드를 짤 수 있게 된다.
  • 스프링에서는 스프링컨테이너에 빈을 등록할 때 스코프 기본값으로 싱글톤 방식을 취한다.
    다만, 이 방식만 지원하는것이 아닌 요청할 때마다 새로운 객체를 생성해서 반환시킬 수도 있다.

싱글톤 방식의 유의점

  • 싱글톤 컨테이너를 사용하더라도 결국은 객체 인스턴스 하나를 공유하는 설계이다.
    위에서도 언급했듯 싱글톤 객체는 절대 상태를 유지해서는 안된다!
    즉, 무상태(stateless)로 설계해야한다.

    1. 특정 클라이언트에 의존적인 필드가 있으면 안된다.
    2. 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
    3. 가급적이면 읽기만 가능해야한다.
  • 스프링 빈 필드에 공유값을 설정하고 그 값을 변경시키는 형태의 코드는 절대 사용해서는 안된다. 공유필드를 실수로 건드리는 순간 한 객체를 공유하는 싱글톤패턴의 특성상 대참사가 발생하게된다! 이는 멀티스레드 환경에서의 안정적 구동, Tread-Safe 보장에 핵심적인 요소이다.

한 예시를 들어보자

@Bean
public class StatefulService {
private int price; //상태를 유지하는 필드
public void order(String name, int price) {

this.price = price;

}
public int getPrice() {
return price;
}
}

위 같은 경우 외부에서

  1. order("오곡코코볼바", 1500);
  2. order("그레놀라바", 2500);

이런 식의 두가지 클라이언트 접근이 있었다 가정하자, 클라이언트는 "오곡코코볼바"의 가격이 1500임을 기대하지만 실제로는 price값은 2500이 반환되게 된다.
이같은 경우처럼 빈 내부에 공유필드 값을 설정하면 여러 접근에 의해 값이 계속해서 바뀔 수 있게된다.

따라서 값을 반드시 변경해야하는 경우 공유필드가 아닌 지역변수, 파라미터등 공유되지 않는것을 적극 사용하자.

@Configuration과 싱글톤

  • @Configuration 은 스프링 컨테이너에 등록된 빈들을 싱글톤 방식으로 관리되게 해준다.
    @Configuration이 붙지 않은 설정자 클래스를 AnnotationConfigApplicationContext의 파라미터로 넘기고, 이를 통해 스프링 컨테이너를 구성한다면 싱글톤 방식이 보장되지 않는다.

  • @Configuration을 설정자 클래스에 붙히게 되면, 스프링은 CGLIB이라는 바이트 조작 라이브러리를 통해 파라미터로 넘겨받은 클래스를 상속받은 임의의 다른 클래스를 만드는데, 이 생성된 클래스가 스프링 빈으로 등록되게 되고 조작된 바이트코드로 인해 순수한 자바코드대로가 아닌 싱글톤 방식의 객체 관리를 하게 된다.

  • CGLIB에 의해 조작된 설정자 클래스는 @Bean을 탐색하며 스프링 빈을 등록하고 반환하는 코드가 동적으로 만들어지게 된다. 이때 만약 이미 등록된 (이미 생성된 객체 인스턴스) 빈을 만나게 된다면, 이미 존재하는 빈은 반환하여 싱글톤을 보장하게된다.

  • 반면, 설정자 클래스에 @Configuration을 적용하지 않고 @Bean만 적용하면 어떨까?

    이러한 경우에서 중복된 메서드를 실행하게 된다면, 가장 처음에는 정상적으로 빈 등록을 실시하지만 이후에는 순수 자바 코드대로 메서드가 호출되며 새로운 인스턴스가 생성되게 된다!

0개의 댓글

관련 채용 정보