[스프링 핵심원리] - 4.싱글톤 컨테이너 (1)

Chooooo·2022년 11월 4일
0
post-thumbnail

이 글은 강의 : 김영한님의 - "스프링 핵심원리 - 기본편"을 듣고 정리한 내용입니다. 😁😁


이번시간부터 싱글톤 컨테이너에 대해 알아볼 것이다.

웹 애플리케이션과 싱글톤

  • 스프링 : 기업용 온라인 서비스 기술을 지원하기 위해 탄생했다.
  • 대부분의 스프링 애플리케이션 = 웹 애플리케이션(웹이 아닌 애플리케이션 개발도 가능하다)
  • 웹 애플리케이션 : 보통 여러 고객이 동시에 요청을 한다.

고객이 요청할 대마다 새로운 객체가 생성되는 문제

스프링 없는 순수한 DI컨테이너 테스트

우리가 만든 스프링 없는 순수한 DI컨테이너(AppConfig)는 요청할 때마다 새로운 객체를 생성한다.

  • 고객 트래픽이 초당 100이 나오면 초당 100개의 객체가 생성, 소멸된다. --> 메모리 낭비가 매우 심함.

😄 해결방안 : 해당 객체를 1개만 생성, 공유하도록 설계

이게 바로 싱글톤 패턴

SingletonTest 코드 작성

package hello.core.singleton;

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;

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

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

        // 위에서 생성한 객체 2개의 참조값이 다른 것을 확인
        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        // memberService1 != memberService2 (두 객체가 다른지) 검증
        Assertions.assertThat(memberService1).isNotSameAs(memberService2);
    }
}
  • Assertions.assertThat(actual).isNotSameAs(expected) : 실제값(actual)이 기대값(expected)와 다른 객체인지 검증

당연히 결과는 appConfig의 memberService()를 호출해 생성된 MemberServiceImpl 2개는 다른 인스턴스다!

싱글톤 패턴

싱글톤 패턴은 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.

객체 인스턴스를 2개 이상 생성하면 안되므로 private 생성자를 사용해 외부에서 임의로 new 키워드를 사용하지 못하도록 한다.

싱글톤 패턴을 적용한 예제 코드

싱글톤 패턴을 구현하는 방법은 여러가지가 있지만 여기서는 객체를 미리 생성해두는 가장 단순하고 안전한 방법을 사용했다.

package hello.core.singleton;

public class SingletonService {
    // 싱글톤 패턴 구현: 객체를 미리 생성해두는 가장 단순, 안전한 방법
    
    // 1. static 영역에 객체를 딱 1개만 생성한다. (외부에서 접근하지 못하도록 private으로)
    private static final SingletonService instance = new SingletonService();

    // 2. Singleton 객체 인스턴스가 필요하면 이 static 메서드를 통해서만 조회하도록 public으로 선언한다.
    public static SingletonService getInstance() {
        return instance;
    }
    
    // 3. private 생성자(외부에서 new로 객체를 생성하는 것을 막기 위함)
    private SingletonService() {
    }

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

1) SingletonService 클래스 static 영역에 객체 instance 1개를 미리 생성해둔다.

2) SingletonService 객체 인스턴스가 필요하면 public으로 선언된 getInstance()를 통해서만 조회할 수 있다. 이 메서드는 항상 같은 인스턴스를 반환한다.

3) 딱 1개의 객체 인스턴스만 존재해야 하므로, 생성자를 private으로 선언해 외부에서 new 키워드로 객체 인스턴스가 생성되는 것을 막는다.

싱글톤 패턴을 사용하는 테스트 코드

package hello.core.singleton;

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

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

public class SingletonTest {
    ...
    
    @Test
    @DisplayName("싱글톤 패턴을 적용한 객체 사용")
    void singletonServiceTest() {
        // private으로 생성자를 막아둠(외부에서 생성하면 컴파일 오류)
        // new SingletonService();
        
        // 1. 조회: 호출할 때 마다 같은 객체 반환
        SingletonService singletonService1 = SingletonService.getInstance();
        // 2. 조회: 호출할 때 마다 같은 객체 반환
        SingletonService singletonService2 = SingletonService.getInstance();

        // getInstance()로 얻은 객체 인스턴스 2개의 참조값이 같은지 확인
        System.out.println("singletonService1 = " + singletonService1);
        System.out.println("singletonService2 = " + singletonService2);

        // singletonServcie1 == singletonService2 (두 객체 인스턴스가 같은지 검증)
        assertThat(singletonService1).isSameAs(singletonService2);

        singletonService1.logic();
    }
}
  • Assertions.assertThat(actual).isSameAs(expected): 실제값(actual)이 기대값(exptected)와 같은 객체인지 검증

결과로는 당연히 두 객체는 같은 것을 확인할 수 있다.

싱글톤 패턴 문제점

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

profile
back-end, 지속 성장 가능한 개발자를 향하여

0개의 댓글