Developement/JVM

[Spring] 싱글톤 패턴이 왜 중요한 걸까?

rt.slowth 2023. 5. 14. 09:07

계기 및 주제


최근에 인프런에서 백기선님의 GoF 디자인패턴 강의와 헤드 퍼스트 디자인 패턴 책을 잡기 시작했다. 가장 처음은 항상 유명한 싱글톤 패턴이었다. Spring 을 학습하는 백엔드 개발자라면 항상 듣는 게 Spring 의 핵심 원리는 싱글톤 패턴이라는 것이다.

 

  • 그렇다면 싱글톤 패턴이 뭐길래 Spring 에서 중요한걸까?
  • 그리고 싱글톤 패턴으로 Spring에서는 무슨 일을 하는 걸까?

Spring 개발자 면접을 볼 때 디자인패턴에 대해서 물어본다면 대부분 싱글톤 패턴이나 팩토리 메소드 패턴을 물어본다.

대체 이 패턴이 Spring 에서 어떤 식으로 작동하기에 기본 개념처럼 물어보는 것인지 나름대로 몰입한 기록을 담아볼 예정이다.

 

 

 

그래서 싱글톤 패턴이 뭔데?


정의는 굉장히 간단하다.

객체의 인스턴스가 오직 단 1개만 생성되는 것이다.

 

 

 

이게 Spring Framework에서 어디에 있는 거야?


package com.pattern.gof.singleton

import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Configuration

@Configuration
class CheckSingletonInSpring {

}

fun main(args: Array<String>){
    val context: ApplicationContext = AnnotationConfigApplicationContext(CheckSingletonInSpring::class.java)
}

 

위에서 간단하게 CheckSingletoninSpring 이라는 클래스를 생성하고 context 라는 변수에 AnnotationConfigApplicationContext 를 인스턴스를 생성한다.

 

실제로 AnnotationConfigApplicationContext는 AbstractApplicationContext 클래스를 상속받아서 ApplicationContext 인터페이스를 구현한 클래스이다. AbstractApplicationContext는 스프링에서 일반적인 애플리케이션 컨텍스트의 공통 기능을 제공하며, AnnotationConfigApplicationContext는 이를 확장하여 자바 구성 클래스와 관련된 기능을 추가한 구현체이다.

 

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

 

위 두 선언부에서 확인할 수 있다.

 

그 중 DefaultListableBeanFactory의 내부에서 Singleton Registry 를 이용해서 구현한 모습을 확인할 수 있다.

 

	@Nullable
	private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
		// 빈 검색
        NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
		
        // 등록되어 있다면 해당 빈의 인스턴스 반환
        if (namedBean != null) {
			return namedBean.getBeanInstance();
		}
        
        // 다른 곳에 있다면 찾아서 반환
		BeanFactory parent = getParentBeanFactory();
		if (parent instanceof DefaultListableBeanFactory dlfb) {
			return dlfb.resolveBean(requiredType, args, nonUniqueAsNull);
		}
		else if (parent != null) {
			ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
			if (args != null) {
				return parentProvider.getObject(args);
			}
			else {
				return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
			}
		}
        
        // 못 찾으면 null
		return null;
	}

 

이는 객체당 하나의 인스턴스를 허용하는 싱글톤 패턴의 의미와 닮았다.

 

 

 

그래서 어떤 게 Singleton Pattern(Singleton Registry) 가 적용된 거야?


@Bean과 @Component 어노테이션은 스프링의 DefaultListableBeanFactory를 통해 빈(Bean)을 등록하고 관리한다.

DefaultListableBeanFactory는 스프링의 기본 빈 팩토리 구현체 중 하나로, BeanFactory 인터페이스를 구현하고 있습니다.

DefaultListableBeanFactory는 빈의 등록, 조회, 생성, 의존성 주입 등을 담당한다.

 

따라서 @Bean 과 @Component 어노테이션을 사용하는 모든 곳은 사실상 싱글톤 패턴을 사용하고 있는 것으로 생각하고 되지 않을까?

 

@Bean 은 개발자가 정의한 메소드를 빈으로 등록할 때 사용한다.
@Component 는 개발자가 정의한 클래스를 빈으로 등록할 때 사용한다.

 

 

 

후기


뭔가 더 부족하게 조사한 느낌이지만, 궁금했던 의문은 많이 풀렸다.

추가할 내용이 있으면 천천히 추가할 예정이다.