spring/core

Spring @RequestScope 빈 스코프

cjswoduddn 2022. 8. 7. 16:48

스프링에서 관리하는 빈의 스코프는 기본값이 싱글톤이다. 웹애플리케이션을 운영하는 경우 사용자마다 다른 데이터를 갖고 서버에 진입할 수 있고 다른 데이터를 갖는다면 싱글턴빈으로는 그 값을 표현할 수 없다. 사용자마다 다른 데이터를 갖는 빈을 마치 스프링이 관리하는 싱글턴 빈처럼 사용할 수는 없을까?

 

@RequestScope 사용

@Component
@RequestScope
@Setter
@Getter
public class HttpRequest{

    private String headerMessage;
    private int count;

}
@Slf4j
@RequiredArgsConstructor
@RequestMapping
@RestController
public class TestController {

    private final HttpRequest request;

    @GetMapping
    public void test(){
        request.setCount(request.getCount()+1);
        log.info("count : {} ", request.getCount());
    }

}

HttpRequest에 @RequestScope 애너테이션을 붙여 스코프를 명시해주고 @Component를 붙여 빈으로 등록해준다. 그다음 빈을 주입받길 원하는 빈 TestController에 원래 하던 것처럼 HttpRequest를 주입해주고 매핑할 메서드에 request의 카운트를 늘려준다.

 

TestController에 주입해준 HttpRequest가 싱글턴이라면 count의 값은 매 요청마다 1씩 늘어나면서 출력할 거고, 매번 새로운 빈이라면 아무리 요청을 계속 넣어도 1만 출력할 거다.

 

curl을 이용해 테스트해보면 count는 계속 1로 찍히는 걸 확인할 수 있다.

 

의문점

근데 곰곰이 생각해보면 TestController에 final키워드로 붙은 HttpRequest에 매번 새로운 빈을 컨테이너가 주입해주는 게 말이 될까? 말 안된다. 로그에 HttpRequest의 해시코드를 추가로 출력해보자.

 

@Slf4j
@RequiredArgsConstructor
@RequestMapping
@RestController
public class TestController {

    private final HttpRequest request;

    @GetMapping
    public void test(){
        request.setCount(request.getCount()+1);
        log.info("hashCode : {}, count : {} ", request.hashCode(), request.getCount());
    }

}

...?

HttpRequest의 해시코드값이 매번 같은 값으로 나온다. 즉, TestController에 최초로 주입된 HttpRequest는 분명 싱글턴으로 동작하고 있다. 생각해보면 curl로 요청이 들어갈 때 HttpRequest의 생명주기가 시작되는데 TestController는 애플리케이션이 부팅되는 시점에 생성되고 그에 맞게 최초 HttpRequest또한 생겨야 정상인 거다. 

 

엄밀히 말하면

디버거를 통해 주입된 HttpRequest를 까보면 이상한 걸 알 수 있다. 결론부터 말하자면 TestController 에 주입된 HttpRequest는 스프링이 HttpRequest에 붙은 애너테이션 @RequestScope를 보고 프록시 싱글턴빈을 생성해 주입한 빈이다. 실제로 우리가 의도한 대로 사용할 HttpRequest(count = 1을 응답해주는)는 빈 사용 시점까지 생성을 지연시킨다.

 

 

마치며

@RequestScope와 Intercepter를 잘 활용하면 웹 애플리케이션의 유저 별 정보나 로그찍기를 깔끔하게 할 수 있을 것 같다. 다만, 비지니스 코드에 직접 빈을 주입하는 건 스프링에 대한 지식이 많이 필요하므로 부적절한 것 같기도 하다.(애초에 스프링 애너테이션은...?)

 

프록시를 생성하는 과정을 좀 더 파보고 싶지만 이 과정 또한 문맥에 맞게 Lazy Propagation하도록 하자 ^^....