2025. 6. 21. 11:54ㆍSpring 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 |