스프링의 bean과 싱글톤의 연관관계

문지은·2023년 3월 12일
1

사건의 발단

현재 회사에서 google api를 활용한 서비스를 만들고 있다. 그래서 제일 처음에는 매번 내가 만든 api를 호출할 때마다 google api를 연결하기 위한 connection 객체를 생성하고 이를 사용해서 google api를 호출하게끔 코드를 구현했다. 하지만 테스트해본 결과 원하는 의도대로 동작은 했지만 속도가 생각보다 너무 느려지는 문제가 발생했다.
이 사태와 관련해서 팀원에게 의견을 물어봤고 팀원은 매 호출마다 connection 객체를 만드는 대신에 connection 객체를 초기에 한번 만들고 이를 bean으로 등록해서 이를 재사용한다면 속도나 메모리 차원에서 절약이 될거라고 피드백을 줬다.
이 피드백을 나는 객체를 싱글톤으로 만들라는 의미로 이해했고 처음에 짠 코드는 이랬다.

public class Connection {
	private Connection connection;
		
	public static Connection getConnection() {
		if(connection == null) {
			connection = new Connection();
		}
		return connection;
	}
}

그리고 돌아온 팀원의 대답은 제가 말한 건 이런게 아니었는데...😅

그리고 다시 스프링 bean의 싱글톤의 관계에 대해 찾아보기 시작했다.

Java 차원의 싱글톤과 Spring 차원의 싱글톤

먼저 내가 짠 코드는 Java 차원에서의 대표적인 싱글톤을 구현하기 위한 코드다. (물론 static 선언도 하지 않았고 synchronized 도 붙이지 않았기 때문에 불완전한 버전이지만..!)

팀원이 내게 피드백을 준 내용은 Spring 차원에서의 싱글톤이었다. 그렇다면 그 둘의 차이점은 무엇일까?

구글링한 결과 하나의 문서를 볼 수 있었다 : Spring의 Singleton과 Java static기반 Singleton패턴의 차이

내가 간략하게 정리해본 내용은 다음과 같다.

  • 자바 레벨 싱글톤 처리 클래스 로더(하나의 jar 파일) 안에서 싱글톤 보장 -> static 필드로 지정해서 thread safe하게 만들 수 있지만 미리 인스턴스를 만들어서 메모리 낭비가 될 수 있고 static이라 테스트도 어려움
  • 스프링 레벨 싱글톤 처리 : 스프링 컨테이너 안에서 싱글톤 보장 -> thread safe. 어노테이션 이용하기 때문에 관리 용이

Spring 차원의 싱글톤 bean 만들기

이제 기존에 내가 짠 코드가 어떤 허점이 있었는지 알았으니 알맞은 버전으로 Spring 차원의 싱글톤 bean을 만들 차례다.

이 때도 난 처음에는 @Component 어노테이션을 활용해서 클래스를 bean으로 등록하면 될 거라고 생각했다. 하지만 나는 외부 라이브러리인 google client api 를 활용하는 메서드를 bean으로 등록해야 했기 때문에 @Configuration@Bean을 써야 된다는 것을 알게 되었다.

참고 자료

위에서 본 참고 자료들의 내용을 간단히 정리하자면 이런 내용이다.

// Component 버전

@Component
public static class Config {

    @Bean
    public SimpleBean simpleBean() {
        return new SimpleBean();	// 매번 새로운 객체를 생성
    }

    @Bean
    public SimpleBeanConsumer simpleBeanConsumer() {
        return new SimpleBeanConsumer(simpleBean());
    }
}

// Configuration 버전

@Configuration
public static class Config {

    @Bean
    public SimpleBean simpleBean() {
        return new SimpleBean();	// 한번 Bean으로 등록해둔 뒤에 해당 객체 재사용
    }

    @Bean
    public SimpleBeanConsumer simpleBeanConsumer() {
        return new SimpleBeanConsumer(simpleBean());
    }
}

@Configuration 이 적용된 클래스 안에서 @Bean 으로 지정된 메서드들은 CGLIB Wrapper에 의해서 래핑되어서 한번 실행이 되면 그 결과는 Bean에 저정된다. 이후에 해당 메서드가 호출되면 해당 메서드를 실행하지 않고 초기에 실행했던 결과를 리턴해준다. 하지만 @Component 가 적용된 클래스 안에서 @Bean으로 지정된 메서드들은 매 호출시마다 이를 실행한다.

정리

  • Java 차원의 싱글톤보다는 Spring 차원의 싱글톤을 이용하자
  • 외부 라이브러리를 사용하는 메서드를 Bean에 담아 재사용하고 싶다면 @Configuration 을 이용하자
profile
백엔드 개발자입니다.

0개의 댓글