웹 스코프는 웹 환경에서만 동작하므로 web 환경이 동작하도록 라이브러리를 추가하자.
build gradle에 아래 코드를 추가해줘서 프로젝트.이름.CoreApplication
의 main 메서드를 실행하면 웹 어플리케이션이 실행되는 것을 확인할 수 있다.
implementation 'org.springframework.boot:spring-boot-starter-web'
spring-boot-starter-web 라이브러리르 추가하면 스프링 부트는 내장 톰켓 서버를 활용해서 웹 서버와 스프링을 함께 실행시킨다.
스프링 부트는 웹 라이브러리가 없으면 우리가 지금까지 학습한 AnnotationConfigApplicationContext
를 기반으로 어플리케이션을 구동한다. 웹 라이브러리가 추가되면 웹과 관련되 추가 설정과 환경들이 필요하므로 AnntationConfigServletWEbServerApplicationContext
를 기반으로 어플리케이션을 구동한다.
@Component
@Scope("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();
System.out.println("[" + uuid + "] request scope bean close: " + this);
}
}
위 코드는 로그를 출력하기위한 간단한 클래스를 한 번 만들어봤다.
로그 출력 포멧 : [UUID][requestURL]{message}
UUID를 사용해서 HTTP req를 구분하였다.
즉, 같은 req이다. 즉, 같은 사용자의 것인 것을 확인할 수 있따.
@Scope(value="request")
를 사용해서 request 스코프롤 지정하였다. 물론이때 위 코드처런 value를 생략해도 무관하다.
HTTP req당 하나씩 생성되고, HTTP req가 끝나는 시점에 소멸된다.
이 빈이 생성되는 시점에 자동으로 @PostConstruct
초기화 메서드를 사용해서 uuid를 생성해서 저장해둔다. 이 빈은 HTTP req당 하나씩 생성되므로, uuid를 저장해두면 다른 HTTP 요청과 구분할 수 있다.
requestURL 필드값은 이 빈이 생성되는 시점에는 알 수 없으므로, 외부에서 setter로 입력받도록 하였다.
그럼 이제 이어서 request scope를 테스트 할 자바 클래스를 만들어보자.
일단 서비스 로직을 대강 만들어주고
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String testId) {
myLogger.log("service id = " + testId);
}
}
@Controller
@RequiredArgsConstructor
public class LogDemoController {
private final LogDemoService logDemoService;
private final MyLogger myLogger;
@RequestMapping("log-demo")
@ResponseBody
public String logDemo(HttpServletRequest request) {
String requestURL = request.getRequestURL().toString();
myLogger.setRequestURL(requestURL);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
HttpServeletReqeust 타입은 자바에서 제공하는 표준 servelt 규약이 있는데 그에 의한 http-req 정보를 받을 수 있다. 즉, 교객 요청 정보를 받을 수 있다.
컨트롤러에 위에서 만든 서비를 붙였다. 그럼 위 코드를 실행시켜보면 어떻게 될까?
-> 에러가 나야 정상이다.
참고
reqeustURL을 MyLogger에 저장하는 부분은 컨트롤러보다는 공통 처리가 가능한 스프링 인터셉터나 서블릿 필터같은 곳을 활용하는 것이 더 좋다.
@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);
myLogger.log("controller test");
logDemoService.logic("testId");
return "OK";
}
}
그래서 현업에서는 아이디를 서비스 넘길 때 같이 넘기고 같이 묶어서 로그를 남겨주면 이제 필터링을 해서 보기 편하다.
간단하게 다시 설명하자면 MyLogger myLogger = myLoggerProvider.getObject();
즉 초기화를 요청했을 때 만들어지면서 @PostConstruct
에서 HTTP req랑 내 req랑 연결을 시키면서 uuid랑 걸리도록 한다. 그 다음 setter로 값을 넣어준다.
참고로 ObjectProvider.getObject()
를 위처럼 Controller에서 호출하든 아니면 아래처럼 Service에서 호출하든 같은 HTTP req라면 같은 스프링 빈이 반환된다.
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
public void logic(String testId) {
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.log("service id = " + testId);
}
}