싱글톤 패턴(Singleton Pattern) : 왜 스프링 컨테이너를 쓰는게 좋을까?

msung99·7일 전
1

Spring

목록 보기
17/19
post-thumbnail

시작에 앞서

본 포스팅은 제 지난 포스팅 시리즈 직접 만들어보며 이해하는 SOLID 원칙과 DI 설계 : 수동으로 직접 의존관계 주입해보기 에 이어서 진행되는 내용입니다. 이전 포스팅을 읽고 오시는 것을 권장드립니다! 이번 포스팅에서는 SOLID 5설계원칙을 지킬 수 있도록 스프링에서 제공해주는 스프링 빈 @Bean, 그리고 싱글톤이라는 것에 대해 다루어보고자 합니다.

객체지향 시리즈 포스팅 진행현황

현재 진행중인 포스팅 내용은 아래와 같습니다. 그 중 4번째 포스팅을 지금 진행해볼까합니다!

  • 객체지향을 아는척하지 말자 : 오해하고 있었던 객체지향의 정체
  • 예제로 이해하는 SOLID 5원칙, 그리고 스프링 DI 컨테이너의 등장
  • 직접 만들어보며 이해하는 SOLID 원칙과 DI 설계 : 수동으로 직접 의존관계 주입해보기
  • 싱글톤(SingleTon) : 왜 스프링 컨테이너를 써야할까?(현재 포스팅)
  • 컴포넌트 스캔과 @Autowired 의 메커니즘 : 필요성에 대해
  • 알면 도움될 컴포넌트 스캔의 다양한 대상들과 DI 에 대한 해결방법

굳이 스프링 Bean 을 등록해야할까?

지난 포스팅에서 DI 컨테이너와 스프링 빈(Bean) 을 통해 외부에서 의존관계를 주입하는 방법에 대해 알아봤습니다. 그런데 예리하신 분은 이런 생각을 할 수 있습니다.

그냥 외부에서 설정정보를 주입할거면, 스프링없이 순수 자바코드로도 의존관계를 주입 가능하지 않을까?

정말 좋은 생각입니다. 그런데, 스프링 컨테이너를 통해 왜 관리하는 것이 더 유리한지 지금부터 설명드리겠습니다. 한 줄 요약을 먼저 해보자면 스프링 컨테이너는 곧 싱글톤 컨테이너로써 동작하기 때문입니다.


싱글톤 패턴 (SingleTon Pattern)

만약 스프링 없는 순수한 자바 코드 기반의 DI 컨테이너를 만들었다고 가정해봅시다.
그 컨테이너는 매 클라이언트로부터 요청이 들어올 때 마다 객체를 새롭게 생성해야합니다.

만일 100명의 고객으로부터 요청이 들어온다면, 순수 DI 컨테이너에 있는 설정정보에 기반하여 100개의 객체가 생성되어야 할겁니다. 즉, 순수 DI 컨테이너를 사용한다면 메모리 낭비가 정말 심합니다.

스프링에서는 해결해줍니다. 싱글톤 패턴이라는 것을 지원하는데, 객체가 딱 1개만 생성되고 공유하도록 설계되어 있습니다.

스프링 컨테이너는 싱글톤 컨테이너입니다. 여러 클라이언트의 요청을 처리 딱 1개의 객체 인스턴스로만 처리합니다.

  • 이미 만들어진 1개의 객체를 여러 고객이 공유해서 사용할 수 있는겁니다.

스프링 컨테이너의 특징

싱글톤 패턴과 관련한 스프링 컨테이너의 특징을 더 나열해보면 아래와 같습니다.

  • 스프링은 기본적으로 Bean 등록 방식이 싱글톤 방식입니다. 즉, 스프링 컨테이너에 특정 Bean 객체 하나가 등록되면, 더 이상 중복되서 여러개가 등록되지 않습니다.


@Configuration 과 싱글톤

그런데 이상합니다. 스프링 빈에 등록되는 인스턴스들은 진짜로 더 중복해서 생성이 안될까요? 정말 모두 같은 인스턴스가 여러 고객들에게 공유되어 사용되는 걸까요? 정답은 스프링은 싱글톤을 어떻게든 반드시 보장해준다는 것입니다.

스프링 컨테이너는 싱글톤 보장을 위해, 각 @Bean이 붙은 메소드마다 Bean이 이미 스프링 컨테이너에 등록되어 있는 경우 새로운 Bean을 생성하지 않고, 스프링 컨테이너에서 찾아서 이미 존재하는 Bean을 반환해줍니다.

다시말해, @Bean 이 붙은 메소드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 새롭게 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어집니다.

지난번에 구현했었던 BeanConfig 설정정보 클래스를 다시 살펴봅시다. 지난 코드에 출력문을 간단하게 넣었습니다.

@Configuration
public class BeanConfig {

    @Bean
    public UserService userService() {
        System.out.println("call userService");
        return new UserServiceImpl(carService(), userRepository());
    }

    @Bean
    public UserRepository userRepository(){
        System.out.println("call userRepository");
        return new UserRepositoryImpl();
    }

    @Bean
    public CarService carService(){
        System.out.println("call carService");
        return new CarServiceImpl(carRepository());
    }

    @Bean
    public CarRepository carRepository(){
        System.out.println("call carRepository");
        return new SonarTarRepository();
    }
}

위 설정정보를 기반으로 스프링부트 애플리케이션을 실행했을 때 "call carRepository" 가 3번 출력되어야 할것같은데, 실제로는 1번만 호출됩니다. 이로써 스프링 컨테이너를 어떻게든 싱글톤을 보장해준다는 것을 알 수 있습니다.


정리

또한 스프링 컨테이너는 아래의 특징도 지닙니다.

  • @Bean 만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지는 않습니다.

따라서 크게 고민할 것 없이, 스프링 설정정보는 항상 @Configuration 어노테이션을 사용하고 스프링 빈을 등록해서 싱글톤을 보장받는 것이 좋습니다.

몰론 직접 개발자가 싱글톤 컨테이너를 구현하는 것도 좋습니다. 다만 주의해야 할점은 무상태(stateless) 로 설계해야합니다. 그렇지 않으면 설계가 위험해지고 서비스 로직이 잘못 돌아갈 가능성이 크기 때문입니다!
(이와 관련해 시간이 되면 따로 포스팅을 다루도록 하겠습니다)


마치며

지금까지 스프링 컨테이너에서 보장해주는 싱글톤에 대해 알아봤습니다. 궁금하신 점이 있다면 댓글로 알려주세요!


참고

docs.oracle.com/SingleTon
spring docs : Configuration
docs.spring.io


추후 포스팅 계획 : 계속 이어지는 내용들

앞서 말씀드렸지만, 저는 객체지향에 대한 내용을 이 포스팅을 마무리로 끝내지 않습니다. 다음 포스팅에서는 컴포넌트 스캔과 자동 의존관계 주입은 왜 필요할까? 에 대해 알아보겠습니다!

profile
꾸준히 성장하는 과정속에서, 제 지식을 많은 사람들과 공유하기 위한 블로그입니다 😉

0개의 댓글