티스토리 뷰

Spring

[Spring] 싱글톤 컨테이너

최MAX 2022. 3. 27. 01:39

웹 애플리케이션과 싱글톤

대부분의 스프링 애플리케이션은 웹 애플리케이션. 웹 애플리케이션은 보통 여러 고객이 동시에 요청을 한다.

문제점: 클라이언트 A, B, C가 memberService를 요청할 때마다 객체를 생성한다. 위 그림에서 객체를 3개 생성하게 된다.

 

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

@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);

    //memberService != memberService2 확인
    Assertions.assertThat(memberService1).isNotSameAs(memberService2);
}

 

실행 결과

memberService1과 2가 다름을 확인. 객체가 새로 생성된다!


싱글톤 패턴

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

test/singleton/SingletonService.java

public class SingletonService {
    // 1. static 으로, 딱 1개만 존재함
    private static final SingletonService instance = new SingletonService();

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

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

private를 활용한다.

 

이후 이전에 작성한 test/singleton/SingletonTest.java을 아래와 같이 작성하고 실행하면

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

실행 결과

private access 오류!

따라서 아래와 같이 수정 후 실행

void singletonServiceTest() {
    SingletonService singletonService1 = SingletonService.getInstance();
    SingletonService singletonService2 = SingletonService.getInstance();
    
    System.out.println("SingletonService1 = " + singletonService1);
    System.out.println("SingletonService2 = " + singletonService2);
}

실행 결과

동일한 객체가 생성되었다!!!

 

못믿겠다면 isSameAs로 동일한 객체인지 확인

Assertions.assertThat(singletonService1).isSameAs(singletonService2);

 

싱글톤 패턴을 적용하면 고객의 요청이 올 때 마다 미리 만들어진 객체를 공유해서 효율적으로 사용한다.


 

싱글톤 패턴 문제점

  • 아래와 같은 코드가 기본적으로 들어간다. (구현하는 코드 자체가 많이 들어간다.)
private static final SingletonService instance = new SingletonService();
public static SingletonService getInstance() {
    return instance;
}
private SingletonService() { }
  • 클라이언트가 구체 클래스에 의존한다. DIP 위반 (클라이언트.getInstance() 등을 사용하므로)
  • 클라이언트가 구체 클래스에 의존하므로 OCP 원칙을 위반할 가능성이 높다.
  • 테스트하기 어렵다.
  • 내수 속성을 변경하거나 초기화하기 어렵다.
  • private 생성자로 자식 클래스를 만들기 어렵다.
  • 결론적으로 유연성이 떨어진다.

스프링 컨테이너가 해결해줄 것이다!!!

 


싱글톤 컨테이너

스프링 컨테이너는 싱글톤 패턴의 문제점을 해결하면서 객체 인스턴스를 싱글톤(1개만 생성)으로 관리한다.

이전에 배운 스프링 빈이 바로 싱글톤으로 관리되는 빈이다.

 

  • 스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
  • 스프링 컨에티너는 싱글톤 컨테이너 역할을 한다. 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라 한다.
  • 싱글톤 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지할 수 있다.
    • 싱글톤 패턴을 위한 지저분한 코드 X / DIP, OCP, 테스트. private로 부터 자유롭게 싱글톤 사용 가능

스프링 컨테이너를 사용하는 테스트 코드

void springContainer() {
    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    MemberService memberService1 = ac.getBean("memberService", MemberService.class);
    MemberService memberService2 = ac.getBean("memberService", MemberService.class);

    System.out.println("memberService1 = " + memberService1);
    System.out.println("memberService2 = " + memberService2);
    
    Assertions.assertThat(memberService1).isSameAs(memberService2);
}

.getBean() 함수를 사용하는구나

 

싱글톤 컨테이너 적용 후

이미 만들어진 객체를 공유해서 효율적으로 사용하는구나!

 

스프링의 기본 빈 등록 방식은 위와 같은 싱글톤이지만, 요청할 때 마다 새로운 객체를 생성해서 반환하는 기능도 제공한다. '빈 스코프'에서 설명할 것!

 

 

[출처] 스프링 핵심 원리 - 기본편, 김영한님

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함