꼬물꼬물

싱글톤 컨테이너 [싱글톤 패턴] 본문

스터디/스프링 핵심 원리 - 기본편

싱글톤 컨테이너 [싱글톤 패턴]

멩주 2022. 9. 17. 14:48

웹 애플리케이션과 싱글톤

  • 객체 인스턴스가 JVM 내에 하나만 존재한다.
  • 스프링은 기업용 론라인 서비스 기술을 지원하기 위해 탄생하며 웹 애플리케이션이다.
  • 웹 애플리케이션은 보통 여러 고객이 동시에 요청한다.

현재는 각각의 memberService가 생성되고 있다.

public class SingletonTest {

    @Test
    @DisplayName("스프링 없는 순수한 DI 컨테이너")
    void pureContainer(){
        AppConfig appConfig = new AppConfig();
        //1. 조회: 호출할 때마다 객체 생성
        MemberService memberService1 = appConfig.memberService();

        MemberService memberService2 = appConfig.memberService();

        System.out.println("memberService1 = "+memberService1);
        System.out.println("memberService2 = "+memberService2);

        // memberService1 != memberService2
        Assertions.assertThat(memberService1).isNotSameAs(memberService2);
    }
}
// 결과
        memberService1 = hello.core.member.MemberServiceImpl@6913c1fb
        memberService2 = hello.core.member.MemberServiceImpl@66d18979 <- 다른 객체를 사용하고 있다.
  • 스프링 없는 순수 DI 컨테이너인 AppConfig는 요청할 때 마다 객체를 새로 생성했다.
  • 고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸된다 <- 메모리 낭비!
  • ▶️ 해당 객체가 하나만 생성되고 공유하도록 설계하자 -> 싱글톤 패턴!

 

싱글톤 패턴

  • 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
  • 객체 인스턴스를 2개 이상 생성하지 못하도록 막는다.
    • private 생성자를 사용해 외부에서 임의로 new 키워드를 사용하지 못하도록 막자
package hello.core.singleton;

public class SingletonService {

    // 자기 자신을 내부에 private 으로 가진다. static을 통해 해당 클래스 내에서만 사용하기 대문에 딱 하나만 생성된다.
    // java 싱행될 때 static 영역이 초기화되고 올라간다.
    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance(){
        return instance;
    }

    // private 생성자를 사용해 외부에서 new로 객체 생성을 막는다.
    private SingletonService(){
    }

    public void logic(){
        System.out.println("싱글톤 객체 로직 호출");
    }
}
    public static void main(String[] args) {
    // 생성자가 private이라 생성 불가
        SingletonService singletonService = new SingletonService();
    }
  1. static 영역에 객체 instance를 미리 하나 생성해 올려둔다.
  2. 이 객체 인스턴스가 필요하면 getInstance()를 통해서만 조회할 수 있다. 해당 메서드는 항상 같은 인스턴스를 반환한다.
  3. 딱 1개의 객체 인스턴스만 존재해야 하므로, 생성자를 private으로 막아 외부에서 new로 객체 인스턴스 생성되는 것을 막는다.
    @Test
    @DisplayName("싱글톤 패턴을 적용한 객체 사용")
    void singletonServiceTest(){
        SingletonService singletonService1 = SingletonService.getInstance();
        SingletonService singletonService2 = SingletonService.getInstance();

        System.out.println("singletonService1 = "+singletonService1);
        System.out.println("singletonService2 = "+singletonService2);
        
        assertThat(singletonService1).isSameAs(singletonService2);
//        Same == 
//        Equal isEqualTo
    }
// 결과
        singletonService1 = hello.core.singleton.SingletonService@2de23121
        singletonService2 = hello.core.singleton.SingletonService@2de23121 <- 같은 객체 인스턴스 반환
  • Same은 ==
  • Equal은 isEqualTo
  • private으로 new 키워드를 막아 new SingletonService()가 실행되지 않는다.
  • 호출할 때마다 같은 객체 인스턴스를 반환하고 있다.
  • + 싱글톤 패턴을 구현하는 방법은 여러가지 있따. 객체를 미리 생성해두는 가장 단순한 방법 선택

 

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

 

🚨 싱글톤 패턴의 문제점

  • 구현하는 자체의 코드가 늘어난다.
  • 의존관계 상 클라이언트가 구체 클래스에 의존 <- DIP 위반
  • 클라이언트가 구체 클래스에 의존해 OCP 원칙을 위반할 가능성이 높다.
  • 테스트가 어려움
  • 내부 속성 변경, 초기화가 어렵다.
  • private 생성자로 자식클래스 생성이 어려움
  • 결론적으로 유연성 떨어짐 -> 안티 패턴으로 불리기도 한다.
스프링 프레임워크가 싱글톤 패턴의 문제점을 제거한다!