이번 편부터는 스프링 빈의 생존 범위를 제어하는 ‘스코프(scope)’에 대해 알아본다.
특히 실무에서 잘못 이해하고 사용하면 큰 문제가 되는 프로토타입 스코프와 웹 스코프도 예제와 함께 꼼꼼히 정리해본다.
빈 스코프란?
스프링 컨테이너는 일반적으로 애플리케이션 시작 시점에 빈을 생성하고, 종료 시점에 함께 소멸시킨다.
이는 기본적으로 빈이 싱글톤 스코프로 등록되기 때문이다.
여기서 스코프(Scope)란, “빈이 존재할 수 있는 생존 범위“를 의미한다.
스프링이 지원하는 주요 스코프
스코프 이름설명
스코프 이름 | 설명 |
singleton | 기본 스코프. 컨테이너 시작~종료까지 같은 인스턴스 사용 |
prototype | 요청할 때마다 새로운 빈 생성. 생성·의존 주입·초기화까지만 컨테이너가 관리 |
request | 하나의 HTTP 요청 동안 유지됨 |
session | 하나의 HTTP 세션 동안 유지됨 |
application | 서블릿 컨텍스트와 동일한 생존 범위 |
프로토타입 스코프란?
@Scope("prototype")
@Component
public class PrototypeBean {
@PostConstruct
public void init() {
System.out.println("PrototypeBean.init");
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean.destroy");
}
}
- ac.getBean(PrototypeBean.class)을 호출할 때마다 새로운 인스턴스를 생성함
- 하지만 스프링 컨테이너는 생성, 의존성 주입, 초기화까지만 관여
- 종료 메서드(@PreDestroy)는 호출되지 않음
즉, 프로토타입 빈의 생애주기는 다음과 같다:
생성 → 의존 주입 → 초기화 → (컨테이너가 관리 종료) → 사용자가 직접 관리해야 함
싱글톤과 프로토타입의 차이 실험
싱글톤 테스트
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class);
SingletonBean bean1 = ac.getBean(SingletonBean.class);
SingletonBean bean2 = ac.getBean(SingletonBean.class);
System.out.println(bean1 == bean2); // true
프로토타입 테스트
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
PrototypeBean bean1 = ac.getBean(PrototypeBean.class);
PrototypeBean bean2 = ac.getBean(PrototypeBean.class);
System.out.println(bean1 == bean2); // false
싱글톤 빈에서 프로토타입 빈을 주입하면?
다음과 같이 싱글톤 빈이 프로토타입 빈을 생성자 주입받는다면?
@Scope("singleton")
@Component
public class ClientBean {
private final PrototypeBean prototypeBean;
public ClientBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public int logic() {
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
이 경우 프로토타입 빈이 하나만 생성되어 매번 같은 인스턴스를 재사용하게 된다.
이것은 프로토타입의 의도와는 어긋나는 상황이다.
해결 방법: 매번 새로 빈을 요청하고 싶다면?
1. ApplicationContext를 이용한 DL
@Autowired
private ApplicationContext applicationContext;
public int logic() {
PrototypeBean prototypeBean = applicationContext.getBean(PrototypeBean.class);
return prototypeBean.logic();
}
→ 하지만 ApplicationContext에 직접 의존하게 되므로 테스트가 어렵고 코드도 지저분해짐
2. ObjectProvider 또는 javax.inject.Provider 사용
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
return prototypeBean.logic();
}
- getObject()를 호출할 때마다 새로운 빈을 반환
- 단위 테스트와 유지보수도 용이
- 실무에서는 주로 ObjectProvider 또는 표준인 javax.inject.Provider를 사용
마무리
- 스프링 빈은 다양한 스코프를 통해 생존 범위를 제어할 수 있다.
- 프로토타입 빈은 생성 이후 스프링이 더 이상 관리하지 않으며, 클라이언트가 책임을 져야 한다.
- 싱글톤 빈에서 프로토타입 빈을 사용하려면 Provider나 DL 방식을 고려해야 한다.
'Spring' 카테고리의 다른 글
[Spring 완전 정복 시리즈] 23편 - 빈 스코프 완전 정복 (3): Provider vs 프록시, 그리고 정리 (1) | 2025.08.01 |
---|---|
[Spring 완전 정복 시리즈] 22편 - 빈 스코프 완전 정복 (2): 웹 스코프와 Provider/프록시 패턴 (0) | 2025.08.01 |
[Spring 완전 정복 시리즈] 20편 - 빈 생명주기 콜백과 초기화 전략 정리 (1) | 2025.07.31 |
[Spring 완전 정복 시리즈] 19편 - 자동 주입 고급 기능과 전략 패턴 적용법 (3) | 2025.07.30 |
[Spring 완전 정복 시리즈] 18편 - 의존관계 자동 주입 방식 완전 정리 (0) | 2025.07.30 |