1. 소개
이 예제에서는 Spring MVC HandlerInterceptor 를 이해하고 올바르게 사용하는 방법에 중점을 둘 것입니다.
2. 스프링 MVC 핸들러
인터셉터를 이해하기 위해 한 걸음 물러나서 HandlerMapping 을 살펴보자 . 이것은 메소드를 URL에 매핑하여 DispatcherServlet 이 요청을 처리할 때 호출할 수 있도록 합니다.
그리고 DispatcherServlet 은 HandlerAdapter 를 사용하여 실제로 메서드를 호출합니다.
이제 전체 컨텍스트를 이해했으므로 핸들러 인터셉터가 등장 합니다. HandlerInterceptor 를 사용하여 요청 을 처리하기 전, 처리한 후 또는 완료 후(보기가 렌더링될 때) 작업을 수행합니다.
인터셉터는 교차 문제에 사용할 수 있으며 로깅, Spring 모델에서 전역적으로 사용되는 매개변수 변경 등과 같은 반복적인 핸들러 코드를 피할 수 있습니다.
다음 몇 섹션에서는 다양한 인터셉터 구현 간의 차이점을 살펴보겠습니다.
3. 메이븐 의존성
인터셉터 를 사용하려면 pom.xml 파일 의 의존성 섹션에 다음 섹션을 포함해야 합니다.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
4. 스프링 핸들러 인터셉터
프레임워크 에서 HandlerMapping 으로 작업하는 인터셉터 는 HandlerInterceptor 인터페이스를 구현해야 합니다 .
이 인터페이스에는 세 가지 주요 메서드가 있습니다.
- prehandle() – 실제 핸들러가 실행되기 전에 호출되지만 뷰는 아직 생성되지 않았습니다.
- postHandle() – 핸들러가 실행된 후 호출
- afterCompletion() – 전체 요청이 완료되고 뷰가 생성된 후 호출 됩니다.
이 세 가지 방법은 모든 종류의 사전 및 사후 처리를 수행할 수 있는 유연성을 제공합니다.
그리고 간단한 참고 사항 - HandlerInterceptor 와 HandlerInterceptorAdapter 의 주요 차이점 은 첫 번째 방법에서는 preHandle() , postHandle() 및 afterCompletion() 의 세 가지 메서드를 모두 재정의해야 하지만 두 번째에서는 필요한 메서드만 구현할 수 있다는 것입니다.
더 진행하기 전에 간단한 참고 사항 – 이론을 건너뛰고 예제로 바로 이동하려면 섹션 5로 바로 이동하세요.
간단한 preHandle() 구현은 다음과 같습니다.
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// your code
return true;
}
메서드는 부울 값을 반환합니다. 이 값은 요청이 핸들러에 의해 추가로 처리되어야 하는지( true ) 아니면 처리되지 않아야 하는지( false ) Spring에 알려줍니다 .
다음으로 postHandle() 구현이 있습니다 .
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
// your code
}
이 메서드는 HandlerAdapter 에서 요청을 처리한 직후 에 뷰를 생성하기 전에 호출 됩니다.
물론 다양한 방법으로 사용할 수 있습니다. 예를 들어 로그인한 사용자의 아바타를 모델에 추가할 수 있습니다.
사용자 정의 HandlerInterceptor 구현 에서 구현해야 하는 마지막 방법 은 afterCompletion()입니다.
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
// your code
}
보기가 성공적으로 생성되면 이 후크를 사용하여 요청과 관련된 추가 통계 수집과 같은 작업을 수행할 수 있습니다.
마지막으로 기억해야 할 사항은 HandlerInterceptor 가 DefaultAnnotationHandlerMapping 빈에 등록되어 @Controller 어노테이션으로 표시된 클래스에 인터셉터를 적용하는 역할을 한다는 것 입니다. 또한 웹 애플리케이션에서 인터셉터를 원하는 수만큼 지정할 수 있습니다.
5. 커스텀 로거 인터셉터
이 예에서는 웹 애플리케이션에 로그인하는 데 중점을 둘 것입니다. 우선, 우리 클래스는 HandlerInterceptorAdapter 를 확장해야 합니다 .
public class LoggerInterceptor extends HandlerInterceptorAdapter {
...
}
또한 인터셉터에서 로그인을 활성화해야 합니다.
private static Logger log = LoggerFactory.getLogger(LoggerInterceptor.class);
이를 통해 Log4J는 로그를 표시할 뿐만 아니라 현재 지정된 출력에 정보를 로깅하고 있는 클래스를 표시할 수 있습니다.
다음으로 사용자 정의 인터셉터 구현에 중점을 두겠습니다.
5.1. 메서드 preHandle()
이 메서드는 요청을 처리하기 전에 호출됩니다. 프레임워크가 핸들러 메서드(또는 다음 인터셉터)로 요청을 더 보낼 수 있도록 true를 반환 합니다 . 메서드가 false를 반환 하면 Spring은 요청이 처리되었으며 추가 처리가 필요하지 않다고 가정합니다.
후크를 사용하여 요청의 매개변수에 대한 정보를 기록할 수 있습니다. 요청의 출처 등
이 예에서는 간단한 Log4J 로거를 사용하여 이 정보를 기록합니다.
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
log.info("[preHandle][" + request + "]" + "[" + request.getMethod()
+ "]" + request.getRequestURI() + getParameters(request));
return true;
}
보시다시피 요청에 대한 몇 가지 기본 정보를 기록하고 있습니다.
여기에서 비밀번호가 발생하는 경우를 대비하여 물론 해당 비밀번호를 기록하지 않도록 해야 합니다.
간단한 옵션은 암호 및 기타 민감한 유형의 데이터를 별표로 바꾸는 것입니다.
다음은 이를 수행하는 방법에 대한 빠른 구현입니다.
private String getParameters(HttpServletRequest request) {
StringBuffer posted = new StringBuffer();
Enumeration<?> e = request.getParameterNames();
if (e != null) {
posted.append("?");
}
while (e.hasMoreElements()) {
if (posted.length() > 1) {
posted.append("&");
}
String curr = (String) e.nextElement();
posted.append(curr + "=");
if (curr.contains("password")
|| curr.contains("pass")
|| curr.contains("pwd")) {
posted.append("*****");
} else {
posted.append(request.getParameter(curr));
}
}
String ip = request.getHeader("X-FORWARDED-FOR");
String ipAddr = (ip == null) ? getRemoteAddr(request) : ip;
if (ipAddr!=null && !ipAddr.equals("")) {
posted.append("&_psip=" + ipAddr);
}
return posted.toString();
}
마지막으로 HTTP 요청의 소스 IP 주소를 얻는 것을 목표로 합니다.
다음은 간단한 구현입니다.
private String getRemoteAddr(HttpServletRequest request) {
String ipFromHeader = request.getHeader("X-FORWARDED-FOR");
if (ipFromHeader != null && ipFromHeader.length() > 0) {
log.debug("ip from proxy - X-FORWARDED-FOR : " + ipFromHeader);
return ipFromHeader;
}
return request.getRemoteAddr();
}
5.2. 메서드 postHandle()
이 후크는 HandlerAdapter 가 핸들러로 호출되지만 DispatcherServlet 이 아직 뷰를 렌더링하지 않을 때 실행됩니다 .
이 메소드를 사용하여 ModelAndView 에 추가 속성을 추가 하거나 핸들러 메소드가 클라이언트의 요청을 처리하는 데 걸리는 시간을 결정할 수 있습니다.
우리의 경우 DispatcherServlet 이 뷰를 렌더링 하기 직전에 요청을 기록하기만 하면 됩니다.
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
log.info("[postHandle][" + request + "]");
}
5.3. 메소드 afterCompletion()
요청이 완료되고 뷰가 렌더링되면 요청 및 응답 데이터와 예외가 발생한 경우에 대한 정보를 얻을 수 있습니다.
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex)
throws Exception {
if (ex != null){
ex.printStackTrace();
}
log.info("[afterCompletion][" + request + "][exception: " + ex + "]");
}
6. 구성
인터셉터를 Spring 구성에 추가하려면 WebMvcConfigurer 를 구현하는 WebConfig 클래스 내에서 addInterceptors () 메서드 를 재정의해야 합니다 .
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor());
}
XML Spring 구성 파일을 편집하여 동일한 구성을 달성할 수 있습니다.
<mvc:interceptors>
<bean id="loggerInterceptor" class="com.baeldung.web.interceptor.LoggerInterceptor"/>
</mvc:interceptors>
이 구성이 활성화되면 인터셉터가 활성화되고 애플리케이션의 모든 요청이 올바르게 기록됩니다.
여러 Spring 인터셉터가 구성된 경우 preHandle() 메서드는 구성 순서대로 실행되는 반면 postHandle() 및 afterCompletion() 메서드는 역순으로 호출됩니다.
바닐라 Spring 대신 Spring Boot를 사용하는 경우 구성 클래스에 @EnableWebMvc 어노테이션을 추가하지 않도록 유의해야 합니다. 그렇지 않으면 Boot의 자동 구성을 잃게 됩니다.
7. 결론
이 예제은 Spring MVC Handler Interceptor를 사용하여 HTTP 요청을 가로채는 것에 대한 빠른 소개입니다.