Spring, HandlerInterceptor(인터셉터) 구현하기

개요

Java EE에는 HTTP 요청에 대한 응답을 수행하는 HttpServlet(@WebServlet)이 존재하며 이러한 서블릿 실행 전후 시점에 임의의 처리를 가능하게 해주는 Filter(@WebFilter)가 존재한다. 필터는 체인 형태로 여러 개를 사용할 수 있다. Spring Web MVC 또한 동일한 기능의 HandlerInterceptor를 제공한다. Filter는 로우 레벨의 처리 로직을, HandlerInterceptor는 회원 인증 검사 등의 비즈니스 레벨의 처리 로직을 작성하는데 적합하다. 이번 글에서는 HandlerInterceptor의 사용 예를 간단히 설명하고자 한다.

HandlerInterceptor 작성

package com.jsonobject.example;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class HttpInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(

            HttpServletRequest request,
            HttpServletResponse response,
            Object handler

    ) throws Exception {

        // HTTP 요청 처리 전 수행할 로직 작성

        return true;
    }

    @Override
    public void postHandle(

            HttpServletRequest request,
            HttpServletResponse response,
            Object handler,
            ModelAndView modelAndView

    ) throws Exception {

        // HTTP 요청 처리 후 수행할 로직 작성
    }
}
  • 인터셉터 클래스를 작성하려면 HandlerInterceptor 인터페이스를 구현해야 한다. 만약 전처리 또는 후처리만 하고자 할 경우 인터페이스의 모든 메써드를 구현하는 것은 번거롭다. 위와 같이 HandlerInterceptorAdapter 추상 클래스를 상속하면 원하는 메써드만 작성할 수 있어 편리하다.

  • 인터셉터 클래스는 @Component 어노테이션을 통해 스프링 빈이 될 수 있다. 따라서 의존성 주입을 통한 비즈니스 로직을 처리하기 편리하다.

WebMvcConfigurer 작성

package com.jsonobject.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class HttpInterceptorConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private HttpInterceptor httpInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(httpInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/public/**");
    }
}
  • 인터셉터 클래스만으로는 아무 일도 일어나지 않는다. 작성한 인터셉터가 작동할 수 있도록 등록해주어야 한다. WebMvcConfigurer 인터페이스를 구현한 @Configuration 클래스를 작성하면 된다. WebMvcConfigurerAdapter 추상 클래스를 상속하여 필요한 메써드만 깔끔하게 작성할 수 있다.

  • 인터셉터를 등록할 때는 인터셉터를 적용할 요청 주소의 패턴과 제외할 요청 주소의 패턴을 명시하여 선택적 적용이 가능하다.

ResponseBodyAdvice 작성

package com.jsonobject.example;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@ControllerAdvice
public class HttpResponseAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(

            MethodParameter returnType,
            Class<? extends HttpMessageConverter<?>> converterType
    ) {

        return true;
    }

    @Override
    public Object beforeBodyWrite(

            Object body,
            MethodParameter returnType,
            MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType,
            ServerHttpRequest request,
            ServerHttpResponse response
    ) {

        // HTTP 요청 처리 후 응답을 가공하는 로직 작성
        response.getHeaders().add("some-header", "some-value");

        return body;
    }
}
  • 인터셉터가 적용된 HTTP 요청을 처리하는 @Controller 클래스의 메써드가 메시지 변환이 필요한 오브젝트를 응답한다면(@ResponseBody, ResponseEntity 오브젝트 반환시) 인터셉터의 후처리 시점에 응답을 가공하는 작업(예를 들면 HTTP 헤더의 추가)이 불가능해진다. 이 경우 ResponseBodyAdvice 인터페이스를 구현한 클래스를 작성하여 해결할 수 있다.

  • beforeBodyWrite 메써드에 후가공이 필요한 로직을 작성한다. 내 경우 HTTP 요청에 대한 트랜잭션 ID를 헤더에 추가하는 로직을 이 부분에 작성하였다.

참고 글

다른 읽을만한 글

저작자 표시 비영리 동일 조건 변경 허락
신고