Spring/김영한 스프링 핵심원리 - 기본편

[섹션5-2] 싱글톤 패턴과 문제점

보름달빵 2024. 2. 15. 22:00

 

 

싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
  • 똑같은 객체 타입의 인스턴스가 2개 이상 생성하지 못하도록 막는 것이다.

How?

private 생성자를 사용해서 외부에서 임의로 new를 하지 못하도록 하는 것이다 

 

 

 

SingletoneService

public class SingletoneService {

    // 1. static 영역에 객체를 딱 1개만 설정해둔다
    private static final SingletoneService instance=new SingletoneService();

    // 2. public으로 열어서 객체 인스턴스가 필요한 경우 static 메서드를 통해서만 이용할 수 있도록 한다
    public static SingletoneService getInstance() {
        return instance;
    }
    //3. 생성자를 private으로 선언해서 외부에서 new를 해서 객체를 생성할 수 없도록 한다. 
    private SingletoneService(){}

    public void logic(){
        System.out.println("싱글톤 객체 로직 호출");
    }
}

 

 

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

 

이렇게 만들어본 SingletoneService가 제대로 동작하는지 테스트 코드를 작성해보자

 

SingletoneServiceTest

@Test
@DisplayName("싱글톤 패턴을 적용한 객체사용")
void singletoneServiceTest(){
    
    new SingletoneService();
}

 

//실행결과
SingletoneService() has private access in hello.core.singletone.SingletoneService

 

역시나 생성자를 private로 설정했기 때문에 외부에서  새로운 객체를 만드는 것은 불가능하다. 

 

 

다음으로  싱글톤 패턴을 적용하여 얻은 객체들은  모두 동일한 객체인지 확인해보자.

 

@Test
@DisplayName("싱글톤 패턴을 적용한 객체사용")
void singletoneServiceTest(){

    // 조회한 객체가 전부 동일한 객체인지 확인하기
    SingletoneService instance1 = SingletoneService.getInstance();
    SingletoneService instance2 = SingletoneService.getInstance();

    System.out.println("instance1 = " + instance1);
    System.out.println("instance2 = " + instance2);

    assertThat(instance1).isSameAs(instance2);
}

 

// 실행결과
instance1 = hello.core.singletone.SingletoneService@52e677af
instance2 = hello.core.singletone.SingletoneService@52e677af

 

테스트 실행결과 호출할 때 마다 같은 객체 인스턴스를 반환하는 것을 확인할 수 있다.  

 

따라서 우리는 싱글톤 패턴을 통해 순수 DI컨테이너의 문제점을 해결 할 수 있다는 것을 알았다.

 

이제 순수 DI 컨테이너인 AppConfig 클래스에 싱글톤 패턴을 적용시킨다면,  하나의 객체를 공유해서 사용할 수 있을 것이다. 

 

 

 

싱글톤 패턴의 문제점


 

싱글톤 패턴을 적용하면 고객의 요청이 올 때 마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있다.

 

하지만 싱글톤 패턴은 다음과 같은 수 많은 문제점들을 가지고 있다.

 

 

 

 

바로 이러한 싱글톤 패턴의 모든 문제점을 해결하면서 객체를 싱글톤으로 관리해주는 것이 스프링 컨테이너이다. 

그렇기에 스프링 컨테이너는 싱글톤 컨테이너 라고도 불린다. 

 

 

 

 

다음 글에서는 스프링 컨테이너 = 싱글톤 컨테이너 에 대해서 더 자세히 알아보자

 

 


☑️ SingletoneServiceTest 전체코드

 

package hello.core.singletone;

import hello.core.AppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class SingletoneTest {


    @Test
    @DisplayName("스프링 없는 순수한 DI컨테이너")
    void pureContainer(){

        AppConfig appConfig= new AppConfig();
        // 1. 조회: 호출할 때 마다 객체 생성
        MemberService memberService1 = appConfig.memberService();
        // 2. 조회: 호출할 때 마다 객체 생성
        MemberService memberService2 = appConfig.memberService();

        // 참조값이 다른것을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        // memberService1 != memberService2
        assertThat(memberService1).isNotSameAs(memberService2);
    }

    @Test
    @DisplayName("싱글톤 패턴을 적용한 객체사용")
    void singletoneServiceTest(){

        // 조회한 객체가 전부 동일한 객체인지 확인하기
        SingletoneService instance1 = SingletoneService.getInstance();
        SingletoneService instance2 = SingletoneService.getInstance();

        System.out.println("instance1 = " + instance1);
        System.out.println("instance2 = " + instance2);

        assertThat(instance1).isSameAs(instance2);
    }

}