웹 애플리케이션과 싱글톤

  • 트래픽이 들어왔을 때 같은 리소스를 여러 트래픽에서 호출하면 각각에 대해 객체가 생성될것이다. TPS가 커질 수록 문제가 발생한다. 
  • 순수 자바 코드의 경우 DI 컨테이너인 AppConfig는 요청할 때마다 객체를 새로 생성한다. 이런 경우 메모리의 낭비가 굉장히 심해진다.
  • 그래서 고안된것이 싱글톤 패턴이다. 
  • private static final SingletonService instance = new SingletonService();
  • 그런데, 위와 같이 코드를 작성하면 어떤 문제가 발생할까? 보통 생성된 싱글톤을 호출하게 되면 .getInstance()를 쓰게 된다. 
  • getInstance()를 쓰게 되면 클라이언트는 구체 클래스 getInstance()에 의존하게 된다. 이는 DIP를 위반하게 되면서 자연스럽게 OCR도 위반한다. 

스프링 컨테이너

  • 스프링 컨테이너라고 부르거나 혹은 싱글톤 컨테이너라고 부른다. 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤
    레지스트리라 한다. 
  • 스프링 컨테이너에 의해서 DIP, OCR, 테스트 (싱글톤 패턴은 테스트 하기 쉽지 않음), private 생성자부터 자유롭게 싱글톤을 사용할 수 있다.
  • 아래 코드에서 스프링 컨테이너에서 각 메서드들에 대해 싱글톤으로 관리하게 된다. 
  • @Test
    @DisplayName("스프링 컨테이너와 싱글톤")
    void springContainer() {
     ApplicationContext ac = new
    AnnotationConfigApplicationContext(AppConfig.class);
     //1. 조회: 호출할 때 마다 같은 객체를 반환
     MemberService memberService1 = ac.getBean("memberService",
    MemberService.class);
     //2. 조회: 호출할 때 마다 같은 객체를 반환
     MemberService memberService2 = ac.getBean("memberService",
    MemberService.class);
     //참조값이 같은 것을 확인
     System.out.println("memberService1 = " + memberService1);
     System.out.println("memberService2 = " + memberService2);
     //memberService1 == memberService2
     assertThat(memberService1).isSameAs(memberService2);
    }
  • 고객의 요청에 따라 객체를 생성하는것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 재사용할 수 있게 되는 장점이 있다. 

싱글톤 객체 설계

  • 싱글톤 객체는 stateful 하게 설계하면 안된다. 즉, stateless 하게 설계해야 한다. 이 말은 값을 수정하면 안되고 읽기만 하도록 처리하는것이 중요하다는 의미다.
  • 특히, 스프링 빈 필드에 공유 값을 설정하면 큰 장애가 발생할 수 있다. 
  • 이 문제를 해결하기 위해서는 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal을 사용해야 한다. 
  • public int order(String name, int price){
       //this.price = price 이렇게 쓰지 말고 아래처럼 바로 리턴해라
       return price; 
    }
  • 싱글톤 객체를 잘 설계했어도 같은 객체를 두개 생성하게 되면 Spring Container에서 아무리 싱글톤을 관리한다고 하지만 싱글톤이 깨지는것처럼 보이게 된다. Spring Container에서는 어떻게 이 문제를 해결하고 있을까?
  • 결과부터 말하면 @Configuration 을 통해서 Spring Container에서 같은 객체에 대한 생성에 대해  AppConfig@CGLB (바이트 코드를 조작한다.) 메서드를 오버라이드해서 로직을 수행하여 같은것에 대해 한번 더 생성하지 않는다. 

김영한님의 스프링 핵심 강의 중

  • 아래는 Sudo 코드이다.
  • @Bean
    public MemberRepository memberRepository() {
    
     if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
     return 스프링 컨테이너에서 찾아서 반환;
     } else { //스프링 컨테이너에 없으면
     기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
     return 반환
     }
    }
  • @Configuration을 떼면 싱글톤이 깨진다. @Bean 처리된 메서드의 호출된 객체들이 모두 생성된다고 보면 된다. (중복) 즉, @Bean만 쓰게 되면 싱글톤을 보장하지 않는다. 여기서 @Autowired 를 쓰게 되면 이 문제를 해결할 수 있는데 이미 한번 호출된것은 자동 주입해주므로써 같은 객체로 처리하게 된다. 

'Spring' 카테고리의 다른 글

의존관계 자동 주입  (0) 2021.12.13
컴포넌트 스캔  (0) 2021.12.12
객체 지향 원리 적용  (0) 2021.12.10
Spring 예제  (0) 2021.12.10
객체지향 설계와 스프링  (0) 2021.12.09