1. 소개

이 예제에서는 Spring MVC HandlerInterceptor 를 이해하고 올바르게 사용하는 방법에 중점을 둘 것입니다.

2. 스프링 MVC 핸들러

인터셉터를 이해하기 위해 한 걸음 물러나서 HandlerMapping 을 살펴보자 . 이것은 메소드를 URL에 매핑하여 DispatcherServlet 이 요청을 처리할 때 호출할 수 있도록 합니다.

그리고 DispatcherServletHandlerAdapter사용하여 실제로 메서드를 호출합니다.

이제 전체 컨텍스트를 이해했으므로 핸들러 인터셉터가 등장 합니다. 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() – 전체 요청이 완료되고 뷰가 생성된 후 호출 됩니다.

이 세 가지 방법은 모든 종류의 사전 및 사후 처리를 수행할 수 있는 유연성을 제공합니다.

그리고 간단한 참고 사항 - HandlerInterceptorHandlerInterceptorAdapter 의 주요 차이점 은 첫 번째 방법에서는 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
}

보기가 성공적으로 생성되면 이 후크를 사용하여 요청과 관련된 추가 통계 수집과 같은 작업을 수행할 수 있습니다.

마지막으로 기억해야 할 사항은 HandlerInterceptorDefaultAnnotationHandlerMapping 빈에 등록되어 @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 요청을 가로채는 것에 대한 빠른 소개입니다.

모든 예제와 구성은 여기 GitHub에서 사용할 수 있습니다 .

Generic footer banner