방금 엄청난 이야기를 들었다.
텀블러를 세척을 잘 안하면 장염에 걸릴 수 있다고 했다...
세제를 써도 뜨거운물로 삶아야한다구 했다... 최근에 뜨거운물로 삶은적이 없는데
혹시 이거때문인가...?
내일 다이소에 가서 과탄산수소를 사서 당장 텀블러 세척을 해야겠다...
난 더러워 🤮🤮🤮
📌 웹 스코프
📌 request 스코프 예제 만들기
✔️ HTTP Request 요청이 들어오고 나갈때까지 동일한 라이프 스타일을 가진다.
spring-boot-starter-web 라이브러리를 추가하면 스프링 부트는
내장 톰켓 서버를 활용해서 웹 서버와 스프링을 함께 실행시킨다.
스프링 부트는 웹 라이브러리가 없으면 우리가 지금까지 학습한
AnnotationConfigApplicationContext 을 기반으로 애플리케이션을 구동한다.
웹 라이브러리가 추가되면 웹과 관련된 추가 설정과 환경들이 필요하므로
AnnotationConfigServletWebServerApplicationContext 를 기반으로 애플리케이션을 구동한다
기대하는 공통 포맷 : [UUID] [requestURL] {message}
UUID
란 : 유니크 아이디, 딱 하나만 생성되는 고유한 아이디requestURL
정보도 추가로 넣어서 어떤 URL을 요청해서 남은 로그인지 확인하자.[d06b992f...] request scope bean create
[d06b992f...][http://localhost:8080/log-demo] controller test
[d06b992f...][http://localhost:8080/log-demo] service id = testId
[d06b992f...] request scope bean close
마이로거 예제
@PostConstruct
초기화 메서드를 사용해서 uuid를 생성해서 저장해둔다.requestURL
은 이 빈이 생성되는 시점에는 알 수 없으므로, 외부에서 setter로 입력 받는다.@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 📌 create:" + this);
}
@PreDestroy
public void close() {
System.out.println("[" + uuid + "] request scope bean 📌 close:" + this);
}
}
서비스 예제
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("service id = " + id);
}
}
컨트롤러 예제
requestURL 값 http://localhost:8080/log-demo
controller test
라는 로그를 남긴다.@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final MyLogger myLogger;
/*
@RequiredArgsConstructor 의존성 주입을 위해 MyLogger myLogger 주입을 하려고 보니까!!
MyLogger는 @Scope(value = "request")로, 리퀘스트 요청에서 존재한다.
따라서 이 빈이 존재하지 않아서 오류가 발생한다!!
⚒️ Probyder을 사용해야한다.
*/
@RequestMapping("log-demo")
@ResponseBody //뷰 화면 없이 문자 바로 반환
public String logDemo(HttpServletRequest request) { //httpRequest 정보를 받을 수 있다.
String requestURL = request.getRequestURL().toString();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
requestURL을 MyLogger에 저장하는 부분은 컨트롤러 보다는
공통 처리가 가능한 스프링 인터셉터나 서블릿 필터 같은 곳을 활용하는 것이 좋다.
여기서는 예제를 단순화하고, 아직 스프링 인터셉터를 학습하지 않은 분들을 위해서 컨트롤러를 사용했다.
스프링 웹에 익숙하다면 인터셉터를 사용해서 구현해보자.
@RequiredArgsConstructor
의존성 주입을 위해 MyLogger myLogger 주입을 하려고 보니까!!
MyLogger
는 @Scope(value = "request")로, 리퀘스트 요청에서 존재한다. 따라서 이 빈이 존재하지 않아서 오류가 발생한다!!
컨트롤러 수정
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final ObjectProvider<MyLogger> myLoggerProvider;
@RequestMapping("log-demo")
@ResponseBody //뷰 화면 없이 문자 바로 반환
public String logDemo(HttpServletRequest request) { //httpRequest 정보를 받을 수 있다.
String requestURL = request.getRequestURL().toString();
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
서비스 수정
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
public void logic(String id) {
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.log("service id = " + id);
}
}
💻 실행화면
[932f6b13-bd88-42a8-b2d9-a1076d08c290] request scope bean 📌
create:hellospring.demo.common.MyLogger@aa45741
[932f6b13-bd88-42a8-b2d9-a1076d08c290][http://localhost:8080/log-demo]
controller test
[932f6b13-bd88-42a8-b2d9-a1076d08c290][http://localhost:8080/log-demo]
service id = testId
[932f6b13-bd88-42a8-b2d9-a1076d08c290] request scope bean 📌
close:hellospring.demo.common.MyLogger@aa45741
proxyMode = ScopedProxyMode.TARGET_CLASS
적용 대상이 클래스 → TARGET_CLASS 를 선택
적용 대상이 인터페이스 → INTERFACES 를 선택
➡️ MyLogger의 가짜 프록시 클래스를 만들어두고 HTTP request와 상관 없이 가짜 프록시 클래스를 다른 빈에 미리 주입해 둘 수 있다.
🤔 주입된 Mylogger를 호출하면?
System.out.println("myLogger = " + myLogger.getClass());
➡️ myLogger = hellospring.demo.common.MyLogger@5ab9de1b
✔️ CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.
MyLogger$ $EnhancerBySpringCGLIB
이라는 클래스로 만들어진 객체가 대신 등록된 것을 확인할 수 있다.✔️ 가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.
마이로거 수정
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) //가짜를 만들어준다?!
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 📌 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) { //httpRequest 정보를 받을 수 있다.
String requestURL = request.getRequestURL().toString();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
서비스 수정
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("service id = " + id);
}
}
💻 실행결과
➡️ 잘 동작된다 😊
-마치 싱글톤을 사용하는 것 같지만 다르게 동작하기 때문에 결국 주의해서 사용해야 한다.
Good~