[Spring 핵심원리 기본편] 웹 스코프
by mini_minSpring 핵심원리 기본편 강의 마지막! 끝 !! 😇
웹 스코프
웹 스코프는 웹 환경에서만 사용이 가능하다. 프로토타입과 다르게 스프링이 해당 스코프의 종료 시점까지 관리한다. 따라서 종료 메서드가 호출된다. (프로토타입은 스프링이 관리는 안해서 종료 메서드 destroy 는 실행하지 않았음)
웹 스코프 종류
request : HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리된다.
session : HTTP session 과 동일한 생명주기를 가지는 스코프
application : 서블릿 컨텍스트와 동일한 생명주기를 가지는 스코프
websocket : 웹 소켓과 동일한 생명주기를 가지는 스코프
웹 환경 추가
웹 스코프는 웹 환경에서만 동작하므로 web 환경이 동작하도록 라이브러리를 추가한다.
implementation 'org.springframework.boot:spring-boot-starter-web'
request 스코프 예제 개발
동시에 여러 HTTP 요청이 오면 정확히 어떤 요청이 남긴 로그인지 구분하기 어렵다.
이럴 때 사용하기 좋은 것이 request 스코프이다.
기대하는 공통 포멧: [UUID][requestURL]{메시지}
UUID, requestURL 을 사용해서 HTTP 요청을 구분하자.
1. Provider 사용하기
MyLogger 클래스
package hello.core.common;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
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 + "]" + "[" +requestURL + "] " + "request scope bean created:" + this);
}
@PreDestroy
public void close(){
System.out.println("[" + uuid + "]" + "[" +requestURL + "] " + "request scope bean close:" + this);
}
}
Controller
package hello.core.web;
import hello.core.common.MyLogger;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService; //의존관계 주입을 받음
private final ObjectProvider<MyLogger> myLoggerProvider; // 의존관계 주입을 받음
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request) {
String requestURL = request.getRequestURL().toString();
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.setRequestURL(requestURL); //요청 URL 받아서 로그에 넣고 찍기
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
Service
package hello.core.web;
import hello.core.common.MyLogger;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
public void logic(String id) {
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.log("service id = " + id);
}
}
출력결과
[dd769dee-cd5e-4fb2-8cd9-b001418c6dd1][null] request scope bean created:hello.core.common.MyLogger@42d95e06 [dd769dee-cd5e-4fb2-8cd9-b001418c6dd1][http://localhost:8080/log-demo] controller test [dd769dee-cd5e-4fb2-8cd9-b001418c6dd1][http://localhost:8080/log-demo] service id = testId [dd769dee-cd5e-4fb2-8cd9-b001418c6dd1][http://localhost:8080/log-demo] request scope bean close:hello.core.common.MyLogger@42d95e06
dd769dee-cd5e-4fb2-8cd9-b001418c6dd1 : UUID 는 모두 같다. 같은 요청임을 알 수 있다.
2. 프록시 모드 사용하기
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
MyLogger 에 proxyMode = ScopedProxyMode.TARGET_CLASS 애노테이션 설정을 추가한다.
위에 코드를 추가하면 CGLIB 라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 생성한다.
결과를 확인해보면 순수한 MyLogger 클래스가 아니라 MyLogger$$EnhancerBySpringCGLIB 라는 클래스로 만들어진 객체가 대신 등록된 것을 확인할 수 있다.
그리고 스프링 컨테이너에 myLogger 라는 이름으로 진짜 대신 가짜 프록시 객체를 등록한다.
그래서 의존관계 주입도 가짜 프록시 객체가 주입된다.
가짜 프록시 객체는 요청이 오면 그 때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.
가짜 프록시 빈은 내부에 실제 MyLogger 를 찾는 방법을 알고 있다.
가짜 프록시 객체는 request 스코프의 진짜 myLogger.logic() 을 호출한다.
가짜 프록시 객체는 원본 클래스를 상속받아서 만들어졌기 때문에 이 객체를 사용하는 클라이언트 입장에서는 원본인지 아닌지도 모르게 동일하게 사용할 수 있다.
✨가짜 프록시 객체는 실제 request scope 와는 관계가 없다.
특징 정리
프록시 객체 덕분에 클라이언트는 마치 싱글톤 빈을 사용하듯이 편리하게 request scope 를 사용할 수 있다.
단지 애노테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체할 수 있다. 다형성과 DI 컨테이너가 가진 큰 장점이다.
클라이언트 코드 변경 필요 없! 다!
꼭 웹 스코프가 아니어도 프록시는 사용할 수 있다.
주의점
마치 싱글톤 처럼 사용되는 것에 주의하자!
특별한 scope 는 꼭 필요한 때에 사용하자. 무분별하게 사용하지 않도록 주의하자.
'Spring' 카테고리의 다른 글
[스프링 MVC 1편] 서블릿 개요 (기본정보) (0) | 2023.06.20 |
---|---|
[스프링 MVC 1편] 웹 애플리케이션 이해 (0) | 2023.06.13 |
[Spring 핵심원리 기본편] 빈 스코프 (0) | 2023.05.31 |
[Spring 핵심원리 기본편] 빈 생명주기 콜백 (객체생성→의존관계 주입) (0) | 2023.05.29 |
[Spring 핵심원리 기본편] 의존관계 자동 주입! ✨ (0) | 2023.05.29 |
블로그의 정보
개발자 미니민의 개발로그
mini_min