본문 바로가기
넓고 얕은 CS 지식

디자인 패턴 - 싱글톤 패턴(Singleton Pattern) 1분 요약 정리(스프링에서의 싱글톤 패턴)

by 팡펑퐁 2023. 3. 4.
728x90

싱글톤 패턴(Singleton Pattern)

  • 애플리케이션이 시작될 때 어떤 클래스가 최초로 한 번만 메모리를 할당하고, 해당 메모리에 인스턴스를 만들어 여러 객체에서 하나의 인스턴스를 참조하여 사용하는 패턴
  • 장점
    • 인스턴스가 필요할 때 기존의 인스턴스를 사용하므로 new를 통해 같은 객체를 여러 개 생성하여 메모리를 낭비하는 것을 방지할 수 있다.
    • 싱글톤으로 구현한 인스턴스는 전역이므로, 여러 객체가 공유하여 사용하는 데에서 오는 여러 장점이 있다.
  • 단점
    • 싱글톤 인스턴스에 많은 객체가 결합되어 있어 결합도가 높아지면 문제가 생겼을 때 에러 복구하기가 굉장히 힘들다
    • 멀티 스레드 환경해서 동기화 처리가 제대로 되지 않으면 인스턴스가 2개 생성되는 문제가 발생할 수 있다는 점이 있다.
    • 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
    • 의존 관계상 클라이언트가 구체 클래스에 의존한다.(DIP 위반)
    • private 생성자로 자식 클래스를 만들기 어려워 결론적으로 유연성이 떨어진다.
스프링없는 순수한 DI 컨테이너
AppConfig appConfig = new AppConfig(); // 1. 조회: 호출할 때 마다 객체를 생성

MemberService memberService1 = appConfig.memberService(); // 1. 조회: 호출할 때 마다 객체를 생성
MemberService memberService2 = appConfig.memberService(); // 2. 조회: 호출할 때 마다 객체를 생성

//참조값이 다른 것을 확인
System.out.println("memberService1 = " + memberService1); 
System.out.println("memberService2 = " + memberService2);

//memberService1 != memberService2
assertThat(memberService1).isNotSameAs(memberService2); // 참조값이 다르게 나온다.

 

 

싱글톤 컨테이너

  • 스프링 컨테이너는 싱글톤 패턴의 많은 문제(단점)를 해결함과 동시에 장점만을 이용할 수 있다.
  • 따로 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
  • 스프링 컨테이너 덕분에 고객의 요청이 올 때마다 객체를 생성하는 것이 아닌, 이미 만들어진 객체를 공유해서 효율적으로 재사용할 수 있다.
  • 스프링의 기본 빈 등록 방식은 싱글톤이지만, 싱글톤 이외의 방식도 지원한다. 

 

 

싱글톤 방식을 사용할 때 주의할 점

  • 싱글톤 패턴, 스프링과 같은 싱글톤 컨테이너를 사용할 때는 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 stateful(상태 유지)하게 설계해서는 안 된다.
  • stateless(무상태)로 설계해야 한다.
    • 특정 클라이언트에 의존적인 필드가 있으면 안 된다.
    • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안 된다.
    • 가급적 읽기만 가능해야 한다.
    • 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);

StatefulService statefulService1 = ac.getBean("statefulService", StatefulService.class);
StatefulService statefulService2 = ac.getBean("statefulService", StatefulService.class);

//ThreadA: A사용자 10000원 주문 
statefulService1.order("userA", 10000);
//ThreadB: B사용자 20000원 주문 
statefulService2.order("userB", 20000);

//ThreadA: 사용자A 주문 금액 조회
int price = statefulService1.getPrice();
//ThreadA: 사용자A는 10000원을 기대했지만, 기대와 다르게 20000원 출력 
System.out.println("price = " + price);
Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000); // true

 

 

@Configuration과 싱글톤

  • @Configuration을 사용하게 되면 스프링이 CGLIB이라는 바이트코드 조작 라이브러리를 사용해서 사용자가 빈으로 등록하려는 클래스가 아닌, 임의의 다른 클래스를 만들어 스프링 빈으로 등록한다.
    • CGLIB은 사용자가 빈으로 등록하려는 클래스의 자식 타입이다. 
  • 이 임의의 다른 클래스는 싱글톤이 보장되도록 도와준다.
  • @Bean만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하디 않으므로, 스프링 설정 정보는 항상 @Configuration을 이용하면 된다.
@Bean
public MemberRepository memberRepository() {
	if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) { 
    	return 스프링 컨테이너에서 찾아서 반환;
	} 
    else { //스프링 컨테이너에 없으면
		기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록 
        return 반환
	} 
}
// 싱글톤이 보장되도록 해줌

 

 

참고 :

https://gyoogle.dev/blog/design-pattern/Singleton%20Pattern.html

김영한 - 스프링 핵심 원리

728x90