jhhan의 블로그

스프링 코어(5) - 싱글톤(Singleton) 패턴 + 컨테이너 본문

Spring

스프링 코어(5) - 싱글톤(Singleton) 패턴 + 컨테이너

jhhan000 2021. 4. 26. 22:32

이번 포스트에서는 싱글톤 패턴에 대해서 알아보겠습니다.

 

싱글톤 패턴은

  • 클래스의 인스턴스가 1개만 생성되는 것을 보장하는 디자인 패턴
  • 2개 이상의 객체가 만들어지지 않게 한다!
  • 간단하게 생각하면 private을 이용해서 new로 생성하지 못하게 할 수 있다.

 

그러면 싱글톤 패턴을 간단하게 적용할 수 있게 해보겠습니다.

singleton 패키지 밑에 SingletonService 자바 파일을 만듭니다.

public class SingletonService {

    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance() {
        return instance;
    }

    private SingletonService() {}

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

이런 식으로 작성합니다.

  • static 영역에 객체 인스턴스를 미리 생성하면
  • 이 객체 인스턴스는 오직 getInstance()라는 메서드를 통해서만 접근이 가능하다.
  • 딱 1개의 객체 인스턴스만 있어야 하기 때문에, 생성자를 private으로 설정
    →외부에서 new를 사용해서 객체 생성이 되지 않음
  • 참고) 객체 생성은 비용이 많이 들기 때문에, 이미 생성된 객체를 공유해서 사용하는 것이 훨씬 효율적이다.

외부에서 생성하면 에러가 뜨는 것을 확인할 수 있습니다.

 

그러면 이제 싱글톤 패턴을 사용하는 테스트 코드를 한번 작성해 보겠습니다.

public class SingletonTest {

    ...

    @Test
    @DisplayName("싱글톤 패턴을 적용한 객체 사용")
    void singletonServiceTest() {
        SingletonService singletonService1 = SingletonService.getInstance();
        SingletonService singletonService2 = SingletonService.getInstance();

        // 실행해보면 같은 인스턴스가 반환된 것을 알 수 있음
        System.out.println("singletonService1 = " + singletonService1);
        System.out.println("singletonService2 = " + singletonService2);

        // isSameAs  : 객체의 참조값이 같은지 비교
        // isEqualTo : 자바에서의 Equals와 동일
        assertThat(singletonService1).isSameAs(singletonService2);
    }
}

호출할 때마다 같은 객체가 반환되는 것을 확인할 수 있습니다.

(싱글톤 패턴을 구현하는 방법은 여러가지라고 합니다. 그 중에서 가장 단순하게 구현할 수 있는 방법을 선택한 것입니다.)

 

이렇게 싱글톤 패턴을 적용하면

여러 요청이 들어오더라도, 이미 만들어진 객체를 공유해서 효율적인 사용이 가능합니다.

하지만 단점도 존재합니다.

  1. 싱글톤 패턴을 구현하는 코드가 많고 길다.
  2. 클라이언트가 구체 클래스에 의존함 → DIP 위반
  3. OCP 위반 가능성 큼
  4. 테스트 어려움
  5. private 생성자 → 자식 클래스 생성이 어려움
  6. 유연성 떨어짐

생각보다 단점이 많습니다.

 

하지만 괜찮습니다.

스프링 컨테이너가 이런 문제점을 해결해 줄 수 있기 때문입니다.

 

특히 스프링 컨테이너는 싱글톤 컨테이너의 역할을 하고

Spring Container == Singleton Container라고 봐도 무방하다고 합니다.

그래서 스프링 컨테이너는 싱글턴 패턴을 따로 적용을 안해도 객체 인스턴스가 싱글톤으로 관리됩니다.

즉, 스프링 컨테이너는 싱글턴 패턴의 단점을 해결해주면서 객체를 싱글톤으로 유지할 수 있게 합니다.

  • 싱글톤 패턴을 위한 코드가 생략이 됨
  • DIP, OCP, 테스트 코드, private 생성자 → 이것들에서 자유롭게 싱글톤 사용이 가능

 

테스트 코드를 한번 보겠습니다.

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

public class SingletonTest {

    // Singleton 컨테이너 관련된 부분
    @Test
    @DisplayName("스프링 컨테이너와 싱글톤")
    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);

        // 실행하면 여기서 오류가 나는 것을 확인할 수 있음
//        assertThat(memberService1).isNotSameAs(memberService2);

        // 이렇게 실행해야 오류 안남
        assertThat(memberService1).isSameAs(memberService2);
    }
}

여기서 테스트를 할 때 

  • 생성된 2개의 객체를 다른 것으로 테스트를 하면 테스트 오류가 납니다.
  • 생성된 2개의 객체를 같다고 테스트를 해야 테스트가 정상적으로 작동합니다.

 

 

결국 스프링 컨테이너가 있기 때문에

요청이 들어올 때마다 객체 생성이 아닌

이미 만들어진 객체를 공유하기 때문에 효율적인 사용이 가능합니다.

 

 

추가적으로 스프링의 빈 등록 방식은 기본적으로 싱글톤입니다.

하지만 싱글톤이 아닌 요청할 때마다 새로운 객체를 생성해서 반환하는 기능도 제공합니다.

대부분의 스프링의 빈 등록 방식이 싱글톤이기 때문에 다른 방식을 굳이 알려고 하지는 않아도 됩니다.

 

이상으로 싱글톤 패턴 + 컨테이너 에 대한 설명을 마칩니다.

 

 

 

 

 

 

출처: 인프런 - 스프링 핵심원리(기본편) by 김영한