본문 바로가기
서버/스프링

[Spring] 빈 스코프 request 클라이언트의 요청을 구분하는 방법

by 베어 그릴스 2022. 8. 28.
320x100

*본 게시글은 김영한님 스프링 핵심 원리 기본편을 보고 이해한 내용을 바탕으로 정리한 글입니다.

 

빈 스코프란?

빈 스코프란 빈이 존재할 수 있는 범위를 뜻한다.

 

스코프를 따로 지정하지 않으면 스프링 빈이 싱클톤으로 생성되어 스프링 빈이 스프링 컨테이너의 시작과 함께 생성되어서 스프링 컨테이너가 종료될 때까지 유지된다.

 

스프링의 3가지 빈 스코프

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

오늘은 3가지 중 웹관련 스코프의 request 스코프 대하여 알아보고자 한다,

 

request 스코프

웹에서 요청이 오는 순간 빈이 생성되고, 해당 요청에 대한 처리가 끝난 순간 빈이 소멸되는 빈 스코프이다.

 

request 스코프는 요청마다 다른 스프링 빈을 만들기 때문에 동시에 여러 http 요청이 오면 정확히 어떤 요청이 남긴 로그인지 구분하기 어려울 때 사용하기에 좋다.

 

웹 통신을 할 것이기 때문에 아래 web 라이브러리를 grdle에 추가해주도록 하자.

implementation 'org.springframework.boot:spring-boot-starter-web'

 


코드로 예를 들어보자.

 

우선 로그를 남기는 용도의 스프링 빈을 하나 만들어 주도록 하자.

해당 스프링 빈을 통해 어떤 URL로 어떤 요청이 왔는지 구분할 수 있게 된다.

 

@Scope를 통해 request 스코프로 바꾸면 해당 빈이 HTTP 요청 당 하나씩 생성되고, HTTP 요청이 끝나는 시점에 소멸된다.

 

request Scope의 스프링 빈을 다른 스프링 빈에서 의존관계 자동 주입을 받는 시점에 스프링 컨테이너에 request Scope의 스프링 빈이 있을까?

 

request Scope의 스프링 빈은 요청이 들어오는 시점에서야 스프링 컨테이너에 생성되기 때문에 의존관계 주입을 받는 시점에서는 존재하지 않는다.

 

그렇다면 어떻게 request Scope의 스프링 빈을 다른 스프링 빈에서 의존관계 주입을 받을 수 있을까?

 

 

 

ObjectProvider를 쓰면 된다.

 

ObjectProvider는 의존관계 자동 주입 시점에 해당 객체를 넣지 않고 ObjectProvider.getObject()가 불렸을 때 비로소 스프링 컨테이너에서 해당 스프링 빈을 찾아오기 때문에 요청 메서드 안에서 해당 스프링 빈을 가져올 수 있다.

 

ObjectProvider는 아래 포스팅에서 이미 한번 사용하였기 때문에 이번엔 proxyMode를 사용하고자 한다.

 

[Spring] 빈 스코프 prototype

*본 게시글은 김영한님 스프링 핵심 원리 기본편을 보고 이해한 내용을 바탕으로 정리한 글입니다. 빈 스코프란? 빈 스코프란 빈이 존재할 수 있는 범위를 뜻한다. 스코프를 따로 지정하지 않으

developbear.tistory.com

 

 

 

 

proxyMode를 사용해서 현재 적용 대상이 class이기 때문에 TARGET_CLASS로 해주었고 만약 interface라면 INTERFACES를 선택하도록 하자.

 

이렇게 하면 requst scope의 가짜 프록시 클래스를 만들어 두고, request와 상관없이 의존 관계 주입 시점에 가짜 프록시 클래스를 다른 빈에 미리 주입해 둘 수 있다.

@Scope 의 proxyMode = ScopedProxyMode.TARGET_CLASS) 를 설정하면 스프링 컨테이너는 CGLIB 라는 바이트코드를 조작하는 라이브러리를 사용해서, MyLogger를 상속받은 가짜 프록시 객체를 생성한다.

 

클라이언트가 myLogger.logic()을 호출하는 것은 가짜 프록시 객체의 logic을 호출하는 것이고, 해당 객체는 실제 요청이 오면 그때 내부에서 실제 빈을 요청하는 위임 로직이 들어있어 실제 스프링 빈의 logic()을 수행한다.

 

 

@Component
@Scope(value = "request",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {

    private String uuid;
    private String requestURL;
	
    //request URL은 빈이 생성되는 시점에선 알 수 없으니 setter로부터 입력 받는다.
    public void setRequestURL(String requestURL) {
        this.requestURL = requestURL;
    }

    public void log(String message){
        System.out.println("[" + uuid + "]"+ "[" + requestURL + "] " + message);
    }

    @PostConstruct
    public void init(){
    	//스프링 빈의 의존관계 주입이 끝나자마자 초기화 작업
    	//요청마다 새로운 ID를 등록해준다.
        uuid = UUID.randomUUID().toString();
        System.out.println("[" + uuid + "] request scope bean create:" + this);
    }

    @PreDestroy
    public void close(){
    	//스프링 빈이 스프링 컨테이너에서 벗어나는 시점에 소멸 작업
        System.out.println("[" + uuid + "] request scope bean close:" + this);
    }
}

 

@Controller
@RequiredArgsConstructor
public class LogDemoController {
    private final LogDemoService logDemoService;
    //가짜 프록시 객체가 대신 주입됨.
    private final MyLogger myLogger;
    
    @RequestMapping("log-demo")
    @ResponseBody //고객 요청 정보를 받는 파라메터
    public String logDemo(HttpServletRequest request){
        String requestURL = request.getRequestURL().toString();
        //MyLogger myLogger = myLoggerObjectProvider.getObject();
		
        System.out.println("myLogger = " + myLogger.getClass());
        myLogger.setRequestURL(requestURL);

        myLogger.log("controller test");
        return "OK";
    }
}

 

결국 빈 스코프의 prototype과 request 스코프에서의 중요한 점은 진짜 객체 조회가 꼭 필요한 시점까지 지연처리한다는 것이다.

 

 

728x90