성실한 사람이 되자

성실하게 글쓰자

This is spear

JAVA_SPRING

SPRING - 웹 스코프

Imaspear 2021. 12. 28. 20:44
728x90

웹 스코프

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

 

웹 스코프 종류

  • request
    • HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리된다.
  • session
    • HTTP Session과 동일한 생명주기를 가지는 스코프
  • application
    • 서블릿 컨텍스트(ServletContext)와 동일한 생명주기를 가지는 스코프
  • websocket
    • 웹 소켓과 동일한 생명주기를 가지는 스코프

request 요청 -> 특정 클라이언트 전용 빈 생성 (request scope) -> http request 가 같으면 전용 빈 (전용 request scope) 에서 활동 즉, Http request 에 따라 각각 정보를 제공해준다.

 

request 스코프 예제 만들어서 이해하기

동시에 여러 HTTP 요청이 오면 어떤 요청이 남긴 로그인지 구분하기 어렵기에 이럴 때 사용하면 좋은 것이 바로 request 스코프이다. 로그 앞에 scope마다 uuid를 사용해서 HTTP 요청을 구분하자.

build.gradle 파일에 스프링 부트 스타터 웹 라이브러리를 넣자

 

    implementation 'org.springframework.boot:spring-boot-starter-web'

 

spring-boot-starter-web 라이브러리를 추가하면 내장 톰켓 서버를 활용해 웹 서버와 스프링을 함께 실행한다. 스프링 부트는 웹 라이브러리가 없으면 우리가 지금까지 학습한 AnnotationConfigApplicationContext 구현체를 기반으로 애플리케이션을 구동했지만 웹과 관련된 추가 설정과 환경을 이용하려면 AnnotationConfigServletWebServerApplicationContext 구현체를 기반으로 사용해야 한다. (물론 이제부터 이 기능을 이용해 애플리케이션이 구동된다.)

 

아래와 같이 설정하면 오류가 난다. 왜냐하면 우리가 요청하는 request scope는 특정한 유저가 응답을 했을 때, 생성이 되고 응답이 끝나면 종료되는 생명 주기를 가지고 있다. 그렇기에 아래와 같이 응답을 받기전에 컨테이너에서 의존관계를 주입하려고 하니 존재하지 않은 빈이라 판단하고 있다.

 

//오류나는 컨트롤러
@Controller
@RequiredArgsConstructor
public class LogDemoController {    
	private final LogDemoService logDemoService;    
	private final MyLogger myLogger;    
	@RequestMapping("log-demo")    
	@ResponseBody    
	public String logDemo(HttpServletRequest request){        
		StringBuffer requestURL = request.getRequestURL();        
		myLogger.setRequestURL(String.valueOf(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);    
	}
}

 

왜 서비스에서 로직을 작성하지 않았냐면 requestURL과 같은 웹과 관련된 정보가 웹과 관련 없는 서비스 계층까지 넘어가면 안된다. 서비스 계층은 웹 기술에 종속되지 않고, 가급적 순수하게 유지하는 것이 유지보수 관점에서 좋다.

 

1. ObjectProvider를 이용한 방법

ObjectProvider를 이용하면 MyLogger를 찾을 수 있는 DL이 주입이 되면서 에러가 나지 않는다.

 

이런 로직은 컨트롤러 보다 공통 처리가 가능한 스프링 인터셉터와 서블릿 핏터에서 사용하자

 

@Controller
@RequiredArgsConstructor
public class LogDemoController {    
private final LogDemoService logDemoService;
	//    private final MyLogger myLogger;    
	private final ObjectProvider<MyLogger> myLoggerObjectProvider;   
	@RequestMapping("log-demo")    
	@ResponseBody    
	public String logDemo(HttpServletRequest request){        
		MyLogger myLogger = myLoggerObjectProvider.getObject();        
		StringBuffer requestURL = request.getRequestURL();        
		myLogger.setRequestURL(String.valueOf(requestURL));        
		myLogger.log("Controller test");        
		logDemoService.logic("testId");        
		return "OK";    
	}
}

 

2. 프록시를 이용한 방법

@Scope 애너테이션의 proxyMode = ScopedProxyMode.TARGET_CLASS 명령어를 설정하면 스프링 컨테이너는 CGLIB 라는 바이트코드를 조작하는 라이브러리를 사용해 MyLogger를 상속받은 가짜 프록시 객체를 생성한다.