본문 바로가기

프로그래밍/Spring

[Spring] 9. 빈 스코프

빈 스코프란?

지금까지 우리는 스프링 빈이 스프링 컨테이너의 시작과 함께 생성되어서 스프링 컨테이너가 종료될 때 까지 유지된다 고 학습했다. 이것은 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 때문이다. 스코프는 번역 그대로 빈이 존재할 수 있는 범위를 뜻한다

 

스프링은 다음과 같은 다양한 스코프를 지원한다.

  • 싱글톤: 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프이다.
  • 프로토타입: 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프이다.
  • 웹 스코프 
    • request: 웹 요청이 들어오고 나갈때 까지 유지되는 스코프이다.
    • session: 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프이다.
    • application: 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프이다웹 관련 스코프

빈 스코프는 다음과 같이 지정할 수 있다

@Scope("prototype")
@Component
public class HelloBean {}

 

수동 등록 

@Scope("prototype")
@Bean
PrototypeBean HelloBean() {
 return new HelloBean();
}

 

 

프로토타입 스코프 - 싱글톤 빈과 함께 사용시 문제점

스프링 컨테이너에 프로토타입 스코프 빈을 요청하면 항상 새로운 객체 인스턴스를 생성해서 반환한다고 했지만, 싱글톤 빈과 함께 사용할 때는 의도한 대로 잘 동작하지 않으므로 주의해야 함

ObjectFactory, ObjectProvider

Provider로 문제 해결

지정한 빈을 컨테이너에서 대신 찾아주는 DL 서비스를 제공하는 것 -> ObjectProvider
(ObjectFactory + 편의 기능 = ObjectProvider)

 

Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
 	PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
 	prototypeBean.addCount();
 	int count = prototypeBean.getCount();
 	return count;
}

 

웹 스코프

지금까지 싱글톤과 프로토타입 스코프를 학습했다. 싱글톤은 스프링 컨테이너의 시작과 끝까지 함께하는 매우 긴 스코 프이고, 프로토타입은 생성과 의존관계 주입, 그리고 초기화까지만 진행하는 특별한 스코프

 

웹 스코프의 특징

  • 웹 스코프는 웹 환경에서만 동작한다.
  • 웹 스코프는 프로토타입과 다르게 스프링이 해당 스코프의 종료시점까지 관리한다. 따라서 종료 메서드가 호출된 다.

웹 스코프 종류

  • request: HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴 스가 생성되고, 관리된다.
  • session: HTTP Session과 동일한 생명주기를 가지는 스코프
  • application: 서블릿 컨텍스트( ServletContext )와 동일한 생명주기를 가지는 스코프
  • websocket: 웹 소켓과 동일한 생명주기를 가지는 스코프

 

  • 스프링 컨테이너에서 request 스코프 의존성 주입 시 오류가 발생할 수 있다.
    • request 스코프는 요청이 들어와야 생성되는데 스프링 컨테이너가 생성되는 시점에는 해당 빈이 컨테이너에 존재하지 않기 때문이다.
  • 이를 해결하기 위해서 Provider 또는 프록시 방식을 사용할 수 있다.
    • 두 가지 방식의 공통점은 진짜 객체 조회를 꼭 필요한 시점까지 지연처리 한다는 점이다.
  • Provider 사용
    • Provider를 이용해서 의존성 주입 시점을 실제로 요청이 들어와서 request 스코프 빈을 사용하는 시점까지 늦출 수 있다면 적절하게 의존성 주입이 가능하다.
     
@Autowired
private final ObjectProvider<MyLogger> myLoggerProvider;
MyLogger myLogger = myLoggerProvider.getObject();

프록시 방식 사용

  • 애노테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체할 수 있다.
    • 이것이 바로 다형성과 DI 컨테이너가 가진 큰 강점
  • 의존성 주입 시점에 request 스코프 빈이 없어도 미리 가짜 프록시 클래스를 의존성 주입해준다. 그리고 실제 사용시점에 실제 객체를 찾는다.
    • CGLIB 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {}
  • 프록시 객체 동작

  • 가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.가짜 프록시 객체는 내부에 진짜 myLogger를 찾는 방법을 알고 있다.
  • 가짜 프록시 객체는 내부에 진짜 myLogger를 찾는 방법을 알고 있다.
  • 클라이언트가 myLogger.logic() 을 호출하면 사실은 가짜 프록시 객체의 메서드를 호출한 것이다. 가짜 프록시 객체는 request 스코프의 진짜 myLogger.logic() 를 호출한다.
  • 가짜 프록시 객체는 원본 클래스를 상속 받아서 만들어졌기 때문에 이 객체를 사용하는 클라이언트 입장에서는 사실 원본인지 아닌지도 모르게, 동일하게 사용할 수 있다(다형성)
  • 가짜 프록시 객체는 실제 request scope와는 관계가 없다. 그냥 가짜이고, 내부에 단순한 위임 로직만 있고, 싱글톤 처럼 동작한다.

    주의

    • 마치 싱글톤을 사용하는 것 같지만 다르게 동작하기 때문에 결국 주의해서 사용해야 한다.
    • 이런 특별한 scope는 꼭 필요한 곳에만 최소화해서 사용해야하며, 무분별하게 사용하면 유지보수하기 어려워진다.

     

    출처 : 김영한 스프링 핵심 원리