[Spring Boot] 싱글톤 컨테이너(Singleton Container)

2024. 12. 30. 18:05·Backend/Spring
반응형

싱글톤(Singleton)

싱글톤이란?

싱글톤(Singleton)은 디자인 패턴 중 하나로, 애플리케이션 내에서 하나의 인스턴스만 생성되도록 보장하는 패턴이다.
즉, 어떤 클래스가 싱글톤으로 정의되면, 해당 클래스의 인스턴스는 애플리케이션 내에서 오직 하나만 존재하게 된다.


싱글톤이 필요한 이유

  1. 자원절약 : 객체를 여러번 생성하지 않고 하나의 인스턴스만 사용. 메모리 자원 절약.
  2. 글로벌 접근 : 애플리케이션 내 어디에서든 동일한 인스턴스를 접근할 수 있음.
  3. 제어된 인스턴스 생명 주기 : 인스턴스 생성/소멸을 제어 -> 애플리케이션의 안정적인 관리.




스프링을 사용하지 않는 DI 컨테이너의 문제점

DI(Dependency Injection) 컨테이너 : 의존성을 관리하고 주입해주는 역할을 함.

스프링을 사용하지 않고 DI(Dependency Injection) 컨테이너를 사용한 예시를 살펴보자.

AppConfig.class

public class AppConfig {
    /* 의존성 주입 */
    public MemberService memberService(){
        return new MemberServiceImpl(getMemberRepository());
    }
    public OrderService orderService(){
        return new OrderServiceImpl(getMemberRepository(), getDiscountPolicy());
    }


    /* 구현체 정의해주기 */
    public MemberRepository getMemberRepository() {
        return new MemoryMemberRepository();
    }
    public DiscountPolicy getDiscountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }
}
        AppConfig appConfig = new AppConfig();
        // 1. 조회: 호출할 때마다 객체를 생성
        MemberService memberService1  = appConfig.memberService();
        // 2. 조회: 호출할 때마다 객체를 생성
        MemberService memberService2  = appConfig.memberService();

        // 두 객체가 참조값이 다름
        System.out.println("memberService1 : " + memberService1);
        System.out.println("memberService2 : " + memberService2);

image

두 객체의 참조값이 다른 것을 확인할 수 있다.

예를 들어, 고객 트래피이 초당 100이 나오면, 초당 100개의 객체가 생성되고 소멸된다.

근데 이 객체가 모두 동일하게 사용되는 객체라면, 심각한 메모리 낭비를 초래한다.




 

스프링 컨테이너

스프링 프레임워크는 기본적으로 싱글톤 스코프를 가지는 빈을 생성하여 관리한다.

즉, 스프링 컨테이너(ApplicationContext) 내에 하나의 빈 정의에 대해 하나의 인스턴스만 생성한다.

@Configuration 어노테이션과 Bean어노테이션을 이용하여 스프링 컨테이너에 빈을 등록할 수 있다.

AppConfig.class

@Configuration
public class AppConfig {
    /* 의존성 주입 */
    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(getMemberRepository());
    }
    @Bean
    public OrderService orderService(){
        return new OrderServiceImpl(getMemberRepository(), getDiscountPolicy());
    }


    /* 구현체 정의해주기 */
    @Bean
    public MemberRepository getMemberRepository() {
        return new MemoryMemberRepository();
    }
    @Bean
    public DiscountPolicy getDiscountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }
}
        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).isSameAs(memberService2);

image




 

CGLIB 프록시

위의 AppConfig.class 예시에서 getMemberRepository에 대해 코드 상으로는 해당 메서드는 Bean에 등록될 때 말고도 memberService, orderService 메서드를 Bean 등록할 때 호출되며, 새로운 MemoryMemberRepository를 생성한다.

하지만 실제 세 메서드에서 사용되는 MemoryMemberRepository는 모두 같은 참조 값을 갖는다.

 

 

CGLIB 프록시
스프링에서 @Configuration 어노테이션이 붙은 클래스는 특별한 처리 과정을 거친다.
이 클래스 내에서 정의된 @Bean 메소드는 싱글톤 빈을 생성하고 반환하는데, 이는 스프링의 CGLIB 프록시를 통해 구현된다.


  1. 스프링은 @Configuration 클래스를 상속받아 프록시 클래스를 생성하고, 이 프록시 클래스가 실제 빈을 관리한다.
  2. @Bean 메소드가 호출될 때마다 새로운 인스턴스를 생성하는 것이 아니라, 이미 생성된 빈을 반환하게 된다.
  3. 프록시 메커니즘: 실제 빈의 메소드를 호출하기 전에, 프록시가 캐시된 빈 인스턴스를 반환한다.




 

싱글톤 주의점

하나의 객체 인스턴스를 생성하여 공유하므로 상태를 유지(stateful)하게 설계하면 안된다.
즉, 무상태(stateless)로 설계해야한다.

❗️ Spring Bean에 공유 값을 설정하면 큰 장애가 발생할 수 있음

  • 특정 클라이언트에 의존적인 필드가 있으면 안된다.
  • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
  • 가급적 읽기만 가능해야 한다.
  • 필드 대신 자바에서 공유되지 않는 지역변수/파라미터/ThreadLocal 등을 사용해야한다.
728x90
반응형

'Backend > Spring' 카테고리의 다른 글

[Spring Boot] Embedded Redis 추가하기 (Mac 해결책)  (0) 2025.02.25
[Spring] 로그 레벨 설정  (1) 2024.12.31
[Spring Boot] Test Code 작성하기  (0) 2024.12.17
[Spring Boot] Application 클래스 (@SpringBootApplication)  (0) 2024.12.16
'Backend/Spring' 카테고리의 다른 글
  • [Spring Boot] Embedded Redis 추가하기 (Mac 해결책)
  • [Spring] 로그 레벨 설정
  • [Spring Boot] Test Code 작성하기
  • [Spring Boot] Application 클래스 (@SpringBootApplication)
settong
settong
    250x250
  • settong
    개 발 자 국
    settong
  • 전체
    오늘
    어제
    • 전체보기 (202)
      • Computer Science (50)
        • Network (7)
        • Operating System (18)
        • Data Structure (9)
        • Database (11)
        • Algorithm (5)
      • Language (17)
        • Java (17)
        • Javascript (0)
        • Python (0)
      • Devops (20)
        • AWS (0)
        • Naver Cloud (16)
        • CICD (3)
        • 웹 서버 관리 (1)
      • Front (0)
        • React (0)
      • Backend (5)
        • Spring (5)
      • 코딩 테스트 정복기 (110)
        • 백준 (51)
        • 프로그래머스 (53)
        • 기타 (6)
      • etc (0)
      • 경제 상식 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    분할정복
    완전탐색
    집합
    다이나믹프로그래밍
    CI/CD
    BFS
    벨만포드
    백준
    ncp202
    Network
    github actions
    프로그래머스
    DFS
    Spring Boot
    해시
    다익스트라
    ncp200
    백트래킹
    ncp
    lcs
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
settong
[Spring Boot] 싱글톤 컨테이너(Singleton Container)
상단으로

티스토리툴바