ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring 기본 14 - 싱글톤 컨테이너
    FrameWork/Spring&Spring-boot 2023. 11. 12. 17:30

    싱클톤 컨테이너 

    웹 애플리케이션과 싱글톤

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

    객체 인스턴스가 해당 프로젝트의 JVM에 하나만 있어야 하는 패턴을 의미한다. 

     

    앞서 게시물에 작성한 코드를 기반으로 위 그림과 같이 고객이 요청을 3번을 하면 3개의 memberService 객체가 만들어진다. 즉 요청이 올때 마다 객체 인스턴스가 만들어 지고 있는 것이다.

     

    package com.hello.core.singleton;
    
    import com.hello.core.AppConfig;
    import com.hello.core.member.MemberService;
    import org.junit.jupiter.api.Assertions;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    
    public class SingletonTest {
    
        @Test
        @DisplayName("스프링 없는 순수한 DI 컨테이너")
        void pureContaier(){
            AppConfig appConfig = new AppConfig();
            // 1. 조회 : 호출할 때 마다 객체를 생성 하는지 확인
            MemberService memberService = appConfig.memberService();
    
            //2 조회 : 호출할 때 마다 객체를 생성 하는지 확인
            MemberService memberService2 = appConfig.memberService();
    
            //참조값이 다른 것을 확인
            System.out.println("memberService = " + memberService);
            //memberService = com.hello.core.member.MemberServiceImpl@24269709
            System.out.println("memberService2 = " + memberService2);
            //memberService2 = com.hello.core.member.MemberServiceImpl@2aceadd4
    
            //memberService != memberService2
            Assertions.assertNotEquals(memberService,memberService2);
    
        }
    }

    위 코드의 결과로 알수 있는것은 spring을 사용하지 않은 순수한 DI컨테이너를(AppConfig)를 생성하면 해당 객체 인스턴스가 호출될 때마다 JWM에 서로 다른  해당 타입의 객체 인스턴스가 생성된다.

    • 위 코드에서 만들어진 스프링 없는 순수한 DI 컨테이너인 AppConfig는 요청을 할 때 마다 객체를 새로 생성한다.
    • 고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸되다. → 메모리 낭비가 심하다.
    • 해결방안은 해당 객체가 딱 1개만 생성되고, 공유하도록 설계하면 도니다. → 싱글톤 패턴

     

    싱글톤 패턴

    • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.
      • 하나의 자바서버안에서 객체 인스턴스가 딱 하나만 생성되는 것이다.
    • 그래서 객체 인스턴스를 2개이상 생성하지 못하도록 막아야한다.
      • private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 한다.

    싱글톤 패턴을 적용한 코드 예제를 main이 아닌 test위치에 생성한다.

    package com.hello.core.singleton;
    
    public class SingletonService {
    
        //자기 자신을 내부에 private static으로 하나 생성
        //static으로 생성하면 클래스 레벨에 올라가 므로 딱 하나만 존재하게 된다.
        private static final SingletonService instance = new SingletonService();
    
        public static SingletonService getInstance(){
            return instance;
        }
    
       private SingletonService(){
            
       }
       
       public void logic(){
           System.out.println("싱클톤 객체 로직 호출");
       }
    }

     

    위 코드와 같이 작성하면 Java코드가 실행될 때 바로 SingletonService 형의 객체 인스턴스를 생성해서 참조 변수 instance에 생성한 객체 인스턴스를 넣어둔다.

     

    그리고 조회를 할때 SingletonService 형을 반환하는 getInstance()메서드를 사용하면 된다.

     

    그리고 외부에서 SingletonService 형의 객체 인스턴스를 생성하지 못하도록  SingletonService 클래스 안에서 기본 생성자를 private으로 막아 버린다.

     

    • 1. static 영역에 객체 instance를 미리 하나 생성해서 올려둔다.
    • 2. 이 객체 인스턴스가 필요하면 오직 getInstance()메서드를 통해서만 조회할 수 있다. 이 메서드를 호출하면 같은 인스턴스를 반환한다.
    • 3. 딱 1개의 객체 인스턴스만 존재해야 하므로, 생성자를 private으로 막아서 혹시라도 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막는다.

     

    싱글톤 패턴 테스트

       @Test
       @DisplayName("싱글톤 패턴을 사용한 객체 사용")
        void singletonServiceTest(){
    
           SingletonService instance1 = SingletonService.getInstance();
           SingletonService instance2 = SingletonService.getInstance();
    
           Assertions.assertSame(instance1,instance2);
           //same - 인스턴스 객체가 실제 물리적을 같아 같은 참조값을 가지는지 비교
           //equal - 인스턴스 객체가 참조값은 달라도 논리적으로 같은 값을 가지는지 비교
    
       }
    • private으로 new 키워드를 막아두었다.
    • 호출할 때 마다 같은 객체 인스턴스를 반환하는 것을 확인할 수 있다.
    참고
    싱글톤 패턴을 구현하는 방법은 여러 가지가 존재하지만 여기서는 객체를 미리 생성해 두는 가장 단순하고 안전한 방법을 선택했다.

     

    객체 인스턴스를 생성하는데 드는 컴퓨터 리소스 비용이 1000이라고 하면 기존에 있는 객체 인스터스를 가져오는데 사용하는 비용은 1정도 이다.

     

    하지만, 스프링 컨테이너를 사용하면 스프링 컨테이너가 객체 인스턴스를 자동으로 싱글톤으로 만들어서 관리해준다.

     

    싱글톤 패턴을 적용하면 고객의 요청이 올 때마다 객체를 생성하는 것이 아니라 이미 만들어진 객체를 공유해서 효율적으로 사용 할 수 있다. 하지만 싱글톤 패텅느 다음과 같은 수많은 문제점들을 가지고 있다.

     

    *싱클톤 패턴 문제점*

    • 싱글톤 패턴을 구현하는 코드 가제가 많이 들어간다.
    • 의존관계상 클라이언트 구체 클래스에 의존한다. → DIP를 위반한다.
      • appConfg에서 @Bean으로 메서드를 등록할때 구체클래스.getInstance()메서드를 사용해서 반환
    • 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다.
    • 테스트 하기 어렵다.
    • 내부 속성을 변경하거나 초기화 하기 어렵다.
    • private 생성자로 자식 클래스를 만들기 어렵다.
    • 결론적으로 유연성이 떨어진다.
    • 안티패턴으로 불리기도 한다.

     

    싱글톤 컨테이너 적용 후

    • 스프링 컨테이너 덕분에 고개의 요청이 올 때 마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 재사용 할 수 있다.
     참고
    스프링의 기본 빈 등록 방식은 싱글톤이지만, 싱글톤 방식만 지원하는 것은 아니다. 요청할 때 마다 새로운 객체를 생성해서 반화하는 기능도 제공한다. (빈 스코프)

     

     

     

    [출처 - 스프링 핵심 원리 - 기본편]

    댓글

Designed by Tistory.