웹 스코프

2025. 6. 21. 11:54Spring Boot

웹 스코프의 특징

※ 웹 스코프는 웹 환경에서만 동작한다.

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

 

웹 스코프의 종류

1. request : HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리된다.

2.session : HTTP Session과 동일한 생명주기를 가지는 스코프

3.application : 서블릿 컨텍스트와 동일한 생명주기를 가지는 스코프

4.websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프

 

웹 스코프는 어떠한 방식으로 움직이는지 살펴보겠습니다.

※우선 2명의 클라이언트 A와 B가 동일한 컨트롤러를 요청했다고 가정하겠습니다. 스프링이 request scope라 인지하고 요청이 들어오면 각각의 인스턴스를 만들어 제공해줍니다. 위 사진과 같이 A 전용, B 전용으로 만들어지며 각각의 주소도 다르게 배정되는 것을 알 수 있습니다. 또한, 서비스 로직에서도 이미 만들어진 것을 바탕으로 반환해주고 있습니다. 결국, HTTP request에 맞춰서 각각 할당되는 것으로 알 수 있습니다.

 

웹 스코프 예제 만들기

웹 스코프를 하기 위해서는 라이브러리를 추가해줘야 합니다.

//web 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-web'

 

참고: spring-boot-starter-web 라이브러리를 추가하면 스프링 부트는 내장 톰켓 서버를 활용해서 웹 서버와 스프링을 함께 실행시킵니다.

 

참고: 스프링 부트는 웹 라이브러리가 없으면 AnnotationConfigApplicationContext를 기반으로 애플리케이션을 구동합니다. 웹 라이브러리가 추가되면 웹과 관련된 추가 설정과 환경들이 필요하므로 AnnotationConfigServletwebServerApplicationContext를 기반으로 애플리케이션을 구동합니다.

 

 

Request 예제 개발

package com.example.Task.common;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.hibernate.annotations.Comment;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.UUID;

@Component
@Scope(value = "request")
public class MyLogger {

    private String uuid;
    private String requestURL;

    public void setRequestURL(String requestURL) {
        this.requestURL = requestURL;
    }

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

    @PostConstruct
    public void init(){
        uuid= UUID.randomUUID().toString();
        System.out.println("["+uuid+"] request scope bean : "+this);
    }
    @PreDestroy
    public void close(){
        System.out.println("["+uuid+"] request scope bean : "+this);
    }
}

 

1.로그를 출력하기 위한 "MyLogger" 클래스이다.

2.@Scope(value = "request")를 사용해서 request 스코프로 지정했다. 이게 이 빈은 HTTP 요청 당 하나씩 생성되고, HTTP 요청이 끝나는 시점에 소멸됩니다.

3.빈이 생성되는 시점에 자동으로 @PostConstruct 초기화 메서드를 사용해서 uuid를 생성해서 저장해둔다. 이 빈은 HTTP 요청 당 하나씩 생성되므로, UUID를 저장해두면 다른 HTTP 요청과 구분할 수 있다.

4.이 빈이 소멸되는 시점에 @PreDestory를 사용해서 종료 메시지를 남긴다.

5.requestURL은 이 빈이 생성되는 시점에 알 수 없으므로, 외부에서 SETTER로 입력 받는다.

 

 

package com.example.Task.common;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequiredArgsConstructor
public class LogController {

    private final LogDemoService logDemoService;
    private final MyLogger myLogger;

    @RequestMapping("log-demo")
    @ResponseBody
    public String logDemo(HttpServletRequest request){
        String requestURL = request.getRequestURI().toString();
        myLogger.setRequestURL(requestURL);

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

}

1.로거가 잘 작동하는지 확인하는 테스트용 컨트롤러 입니다.

2.여기서 HttpServletRequest를 통해서 요청 URL을 받았습니다.

   -requestURL 값: http://localhost:8080/log-demo

3.받은 requestURL 값을 mylogger에 저장해두고 mylogger는 HTTP 요청 당 각각 구분되므로 다른 HTTP 요청 때문에 값이 섞이는 걱정은 하지 않아도 된다.

4.컨트롤러에서는 controller test라는 로그를 남깁니다.

 

package com.example.Task.common;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class LogDemoService {

    private final MyLogger myLogger;
    public void logic(String id) {
        myLogger.log("service id = "+id);
    }
}

1.서비스 계층에서도 로그를 출력

 

결과

-이러한 방식으로 스프링 애플리케이션을 실행 시키면 오류가 발생합니다. 

스프링 애플리케이션을 실행하는 시점에 싱글톤 빈은 생성해서 주입이 가능하지만, request 스코프 빈은 아직 생성되지 않습니다. 이 빈은 실제 고객의 요청이 와야 생성할 수 있습니다.

 

 

ObjectProvider 

-ObjectProvider로 조회를 하면 오류를 해결할 수 있습니다. ObjectProvider 덕분에 ObjectProvider.getObject()를 호출하는 시점까지 request scope 빈의 생성을 지연할 수 있습니다.

- ObjectProvider.getObject()를 호출하는 시점에는 HTTP 요청이 진행중이므로 request scope 빈의 생성이 정상 처리된다.

- ObjectProvider.getObject()를 LogDemoController, LogDemoService에서 각각 한번씩 따로 호출해도 같은 HTTP 요청이면 같은 스프링이 반환된다.

 

 

스코프와 프록시

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

-적용 대상이 인터페이스가 아닌 클래면 TARGET_CLASS 선택

-적용 대상이 인터페이스면 INTERFACES 선택

 

'Spring Boot' 카테고리의 다른 글

Spring JPA 값 타입  (0) 2025.06.23
빈 스코프  (0) 2025.06.20
서블릿 필터  (0) 2025.04.10
로그인 구현(Java + JPA + MySQL)  (0) 2025.02.24
JPA Entity 기본키 매핑  (0) 2025.02.06