Daily Pogle

[Spring] 싱글톤패턴과 스프링 본문

백엔드 기술/Spring

[Spring] 싱글톤패턴과 스프링

pogles 2023. 11. 7. 16:17

스프링은 디자인 패턴으로 싱글톤 패턴을 사용하고 있다. 왜 싱글톤 패턴을 채택했을까?

스프링이 나온이유와 활용하고 있는 분야에 대해서 생각하면 이해하기 쉽다.

 

  • 스프링은 기업용 온라인 서비스 기술을 지원하기 위해 나왔다.
  • 대부분 웹 애플리케이션 개발을 위해 발전했다.
  • 웹 애플레케이션은 많은 사용자들의 동시 요청이 들어온다.

 

이전 싱글톤 패턴을 적용하지 않은 순수 자바를 이용한 DI Containter를 살펴보자

class Web {

        AppConfig appConfig = new AppConfig();
        // user 1 : mebmerService 사용
        MemberService memberService1 = appConfig.memberService();

        // user 2 : mebmerService 사용
        MemberService memberService2 = appConfig.memberService();

        // user 3 : mebmerService 사용
        MemberService memberService3 = appConfig.memberService();


        // user1 은 정보조회, user2 은 정보수정, user 3 은 정보조회 등을 동시에 요청할 경우
        void request() {
            memberService1.getMember();
            memberService2.updateMember();
            memberService3.getMember();
        }
        // ...
}
  • 이용자인 user 가 동시에 같은 service 에서 각 기능을 호출한다고 가정하면, n 명의 사용자가 memberService 에 있는 기능을 동시 요청했을 때, 최소 n개의 memberService 인스턴스가 생생되야한다.
  • 이용자 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸되므로 메모리 낭비가 심하다.
  • 메모리 낭비를 해결하기 위해서 하나의 memberService 객체를 생성하여 공유하도록 설계하는 싱글톤 패턴 을 사용하였다.

스프링의 싱글톤 컨테이너

 

스프링은 기존 싱글톤 패턴에서 나타나는 문제점들을 해결하여 사용할 수 있도록 설계하였다.

  • 스프링 컨테이너는 싱글턴 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
  • 싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 된다.
  • DIP, OCP, 테스트, private 생성자로 부터 자유롭게 싱글톤을 사용할 수 있다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// 스프링 컨테이너 설정정보 클래스 예시
@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    //...
}
class Web {
    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    // 조회: 호출할 때 마다 같은 객체를 반환 - 서로 달라보여도 같은 인스턴스 재사용
    MemberService memberService1 = ac.getBean("memberService", MemberService.class);
    MemberService memberService2 = ac.getBean("memberService", MemberService.class);
    MemberService memberService3 = ac.getBean("memberService", MemberService.class);

    // user1 은 정보조회, user2 은 정보수정, user 3 은 정보조회 등을 동시에 요청할 경우
    void request() {
        memberService1.getMember();
        memberService2.updateMember();
        memberService3.getMember();
    }
}

스프링 싱글톤 컨테이너 주의점

  • 싱글톤 방식의 프로그래밍은 객체 인스턴스 하나만을 공유하여 사용하기 때문에, 싱글톤 객체는 유지(Stateful)하게 설계해서는 안된다.
  • 항상 무상태(stateless)로 설계해야한다.

무상태로 설계하기 위해서는 다음과 사항을 지켜야한다.

  1. 특정 클라이언트에 의존적인 필드가 있으면 안된다.
  2. 특정 클라이언트에 의해 객체내 필드값이 변경되는 코드가 있어서는 안된다.
  3. 가급적 읽기만 가능해야한다, 변경이 불가능하게 끔해야한다.
  4. 객체의 필드 대신에 Java 에서 공유되지 않는 지역변수, 파라미터, 쓰레드로컬 등을 사용해야한다.

** 스프링 빈(객체)의 필드에 공유값을 설정하면 시스템에 큰 장애가 발생할 수 있다.


@Configuration

 

@Configuration 은 스프링 컨테이너가 해당 애노테이션을 사용한 클래스를 설정 클래스로 지정한다고 하였다. 하지만 @Configuration 는 더 중요한 역할을 맡고있다.

 

@Configuration 은 스프링 빈이 싱글톤을 유지할 수 있도록 스프링 빈에 등록된 클래스의 바이트 코드를 조작해준다.

 

CGLIB라는 바이트코드 조작 라이브러리를 사용해서 스프링 빈에 등록할 클래스를 전달받아 해당 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록해준다.

 

이때 임의로 상속받은 클래스를 실제 스프링 빈으로 등록하여, 싱글톤을 보장할 수 있도록 해준다.

 

만약 설정 클래스(AppConfig.class)에서 @Configuration 을 없애고 @Bean 만 사용할 경우, @Bean 으로 등록된 스프링 빈은 서로 다른 인스턴스를 가지고 있는 것을 확인 할 수 있다.