Spring

컨트롤러 요청/응답 가로채기, 스프링 인터셉터(Interceptor)

Luti 2025. 5. 13. 16:09


취미로 계속 만지고 있는 사이트에서 일별 사용자 수를 모니터링할 수 있으면 좋겠다고 생각했습니다.

여러 가지 방법이 있겠지만 저는 프론트에서 GA를 붙이는 방법과 백엔드에서 직접 DB에 로깅을 하는 방법 두 가지를 시도했습니다.

DB에 직접 로깅하는 방식에 대해 고민하고 알아보면서 공부하게 된 스프링 인터셉터에 대해 정리하고자 글을 남겨놓게 되었습니다. 

 

 

 

# 00. Interceptor?

 

인증·인가, 로깅 등 스프링에 매핑된 모든 핸들러 요청에 대해 일관적인 처리를 해야 할 때가 있습니다.  
이때 인터셉터를 활용하면, 컨트롤러 실행 전후에 공통 로직을 손쉽게 적용할 수 있습니다.

인터셉터는 스프링 영역에서 관리하는 인터페이스로, 사전에 정한 특정 컨트롤러에 대한 HTTP 요청/응답을 가로채어 개발자가 원하는 동작을 수행하도록 합니다.

 

이를 통해 코드 중복을 줄이고 공통 동작을 중앙에서 관리함으로써 시스템의 유지보수성을 향상시킬 수 있습니다.

 

 

# 01. Interceptor 구조 / 동작 흐름

 

인터셉터 동작이 포함된 요청부터 응답까지의 동작 흐름은 다음과 같습니다.

 

1. Dispatcher Servlet 진입

모든 요청은 스프링 MVC의 중앙 프론트 컨트롤러인 DIspatcherServlet으로 전달됩니다.

 

2. HandlerMapping을 통한 핸들러 검색

등록된 HandlerMapping 구현체들이 순차적으로 URL 패턴과 일치하는 핸들러(컨트롤러)를 찾습니다.

일치하는 핸들러와, 그에 매핑된 인터셉터 체인을 리턴합니다.

 

인터셉터 체인이란 하나의 HTTP 요청에 대해 적용될 여러 개의 HandlerInterceptor를 순서대로 묶어서 실행하는 구조를 말합니다.

 

3. HandlerInterceptor - preHandle

찾은 핸들러와 연결된 인터셉터들이 preHandle 메서드를 호출하여 요청을 가로챕니다.

 

4. 핸들러 실행

모든 인터셉터의 preHandle이 true를 반환하면, 체인의 마지막에 담겨 있는 실제 핸들러(컨트롤러)가 실행됩니다.

만약 어느 한 인터셉터가 false를 반환하면, 체인 실행이 중단되고 이후 핸들러도 호출되지 않습니다.

 

5. HandlerInterceptor - PostHandle

핸들러(컨트롤러) 실행이 성공하면, 해당 인터셉터들의 postHandle을 역순으로 호출합니다.

 

6. View 렌더링

DispatcherServlet은 View 이름을 ViewResolver에 전달합니다.

이후 실제 View 객체(JSP, Thymeleaf 템플릿 등)를 선택한 뒤 render 합니다.

만약, 프론트엔드를 별도로 두고 스프링에서는 REST API만 반환한다면 VIew 렌더링 단계는 실행되지 않습니다.

 

7. HandlerInterceptor - afterCompletion

응답이 완료되면 인터셉터들이 afterCompletion을 역순으로 호출합니다.

 

8. 클라이언트 응답 수신

응답이 전송되어 브라우저에 표시되거나, API 클라이언트가 결과를 받습니다.

 

 

# 02. Handler Interceptor 메서드

 

DIspatcherServlet은 여러 개의 인터셉터와 함께 핸들러를 실행 체인(execution chain)으로 처리합니다.

핸들러는 체인의 마지막에 위치합니다.

 

PreHandle

 

핸들러 실행 전의 인터셉션 지점입니다.

HandlerMapping이 적절한 핸들러 객체를 결정한 후, HandlerAdapter가 핸들러를 호출하기 전에 이 메서드가 호출됩니다.

 

매개변수(Parameters):

  • request – 현재 HTTP 요청
  • response – 현재 HTTP 응답
  • handler – 실행할 핸들러

반환값(Returns):

  • true인 경우, 다음 인터셉터 또는 핸들러 자체로 실행 체인을 계속 진행합니다.
  • false인 경우, 이 인터셉터가 이미 응답을 처리했다고 DispatcherServlet이 간주합니다.

예외(Throws):

  • Exception – 오류 발생 시

 

PostHandle

 

핸들러가 성공적으로 실행된 후, DispatcherServlet이 뷰를 렌더링 하기 전에 호출되는 인터셉션 지점입니다.

이 메서드는 실행 체인의 역순으로 적용됩니다.

 

매개변수(Parameters):

  • request – 현재 HTTP 요청
  • response – 현재 HTTP 응답
  • handler – 비동기 실행을 시작한 핸들러(또는 HandlerMethod)
  • modelAndView – 핸들러가 반환한 ModelAndView (null일 수 있음)

예외(Throws):

  • Exception – 오류 발생 시

 

afterCompletion

 

요청 처리 완료 후, 즉 뷰 렌더링이 끝난 뒤 호출되는 콜백입니다.

preHandle이 true를 반환하는 경우에만 호출되며, postHandle과 마찬가지로 체인에 등록된 각 인터셉터가 역순으로 호출됩니다.

 

매개변수(Parameters):

  • request – 현재 HTTP 요청
  • response – 현재 HTTP 응답
  • handler – 비동기 실행을 시작한 핸들러(또는 HandlerMethod)
  • ex – 핸들러 실행 중 발생한 예외 (예외 리졸버로 처리된 경우는 제외)

예외(Throws):

  • Exception – 오류 발생 시

 

# 03. Interceptor 등록 방법

 

인터셉터는 WebMvcConfigurerer 인터페이스를 구현한 클래스에서 addInterceptors를 오버라이딩하여 사용할 수 있습니다.

 

preHandle 등 메서드를 구현한 인터셉터를 등록합니다.

`addPathPatterns`를 통해 해당 인터셉터가 적용될 경로를 지정해 주시면 되고, excludePathPatterns에서 인터셉터를 적용하지 않을 경로를 지정할 수도 있습니다.

 

 

 

# 04. Interceptor vs Filter

 

 

인터셉터와 필터 모두 요청/응답 흐름에 개입하여 공통적인 로직을 수행하지만 동작 위치, 역할 등에서 차이가 있습니다.

 

인터셉터의 경우 스프링 MVC 영역에서 동작하며, DIspatcherServlet 진입 이후 핸들러(컨트롤러) 호출 전/후에 동작합니다.

반면, 필터의 경우 스프링이 아닌 서블릿 스펙이며, DispatcherServlet에 진입하기 전/후에 동작합니다.

 

주로 인터셉터의 경우 인증/인가, 로깅, 핸들러 전/후 처리에 사용되며,

필터의 경우 인코딩, CORS, 보안 등을 용도로 사용합니다.

 

또한 인터셉터의 경우 스프링에 매핑된 핸들러 요청에 대해서만 동작을 하지만,

필터는 모든 서블릿 요청을 처리합니다.

 

마지막으로 인터셉터는 요청/응답 객체에 대해 조작이 불가능하지만,

필터에서는 가능하다는 차이도 있습니다.

 


 

일주일에 하나씩 포스트 작성하는게 목표인데... 조금씩 주기가 길어지고 있습니다.

이번 주 주말에 무조건 하나 더 업로드하는 걸 목표로...