인터셉터란?
- Dispatcher Servlet가 Controller를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있게하는 기능을 제공하는 클래스이다.
- Interceptor는 Spring 스펙, Filter는 Servlet 스펙이다.
- Web Conatiner가 아닌 Spring Container에서 관리된다.
실행시점
Interception point before the execution of a handler. Called after HandlerMapping determined an appropriate handler object, but before HandlerAdapter invokes the handler.

- DispatcherServlet은 Handler Mapping을 통해 적절한 Controller(handler)가 반환된다.
- 이는 HandlerExecutionChain 형식으로 반환되며, 여러 개의 Interceptor들이 함께 포함되어 있다.
- 각 인터셉터들의 함수가 수행되며, 이는 함수에 따라 Controller 실행 전이나 후에 실행된다.
- EX) prehandle, posthandle, aftercompletion
[Controller 실행 전 (prehandle 메서드)]
- 반환 값
- false : 더이상의 Interceptor와 Controller 호출이 되지 않는다. (HandlerExecutionChain이 중단된다.)
- true : Execution Chain은 계속해서 수행된다.
- handler (파라미터)
- @RequestMapping이 붙은 메소드의 정보를 추상화한 객체
- HandlerMapping이 찾아준 컨트롤러 빈에 매핑되는 HandlerMethod라는 새로운 타입의 객체
- DispatcherServlet은 애플리케이션이 실행될 때 모든 컨트롤러 빈의 메소드를 탐색하여 매핑 후보가 되는 메소드들을 추출한 뒤, 이를 HandlerMethod 형태로 저장
- HandlerMapping을 통해 요청을 처리할 컨트롤러를 선택하는데, 해당하는 컨트롤러 메서드 정보를 참조할 수 있다.
- 빈 정보, 파라미터, 어노테이션, 리턴 값 등

- 앞에서 언급했듯이, ExecutionChain에서는 컨트롤러 수행 전, 각 인터셉터의 prehandle함수를 호출하며 순서대로 인터셉터들을 실행한다.

HandlerInterceptor 리스트가 저장되어 있다. 
HandlerExecutionChain의 applyPreHandle 메서드 - 만약 return 값이 false인 경우 뒤에서 설명할 afterCompletion 함수만 수행하고 뒤에 위치한 인터셉터들이 더 이상 수행되지 않는다.
[Controller 실행 후 - posthandle 메서드]

- 반환 값 true, false의 의미는 prehandle과 동일하다.
- 당연하지만, 인터셉터들의 prehandle 메서드가 모두 true를 반환했어야 한다. 아니라면, Controller 수행 자체가 안되었을 것이다.
- Controller 및 하위 계층(Service, Repository 등)에서도 작업을 진행하다가 중간에 예외가 발생하면 postHandle은 호출되지 않는다.
- 추가로, ModelAndView 타입의 파라미터가 제공되는데, 최근에는 Json 형태로 데이터를 제공하는 RestAPI 기반의 컨트롤러(@RestController)를 만들면서 자주 사용되지는 않는다.
- 등록된 인터셉터의 순서의 역순으로 수행된다. (컨트롤러 수행 이후에 수행되다보니 당연하다)

[Controller 실행 후 - afterCompletion 메서드]

- HandlerExecutionChain 실행 중간에 예외가 발생하더라도 afterCompletion은 반드시 호출된다.
- 모든 뷰에서 최종 결과를 생성하는 일을 포함해 모든 작업이 완료된 후에 실행된다.
- 요청 처리 중에 사용한 리소스를 반환할 때 사용하기에 적합하다.
쓰임 (+ Filter와의 비교)
- 로깅, 인증 & 인가 등에 사용될 수 있다.
- 다만, 로깅 및 인증, 인가는 서블릿 단의 Filter에서 처리하는 것이 적절하다 생각한다.
- 로깅
- 사용자의 요청 & 응답과 가장 가까운 거리에 있기 때문에 Filter에서 처리하는 것이 더 유용할 것이다.
- Request - 클라이언트 요청 바로 직후, Response - 클라이언트 응답 바로 직전에서 로깅
- 인증 & 인가
- Spring Security FilterChain과의 의존성과 별개로, 보안 관련된 작업을 인터셉터 내부에서까지 끌고 올 필요는 없다 생각한다. 보안적으로 유효하지 않은 클라이언트의 요청은 최대한 빠르게 걸러내는 것이 보안적으로 좋을 것이다.
- 로깅
- 다만, 로깅 및 인증, 인가는 서블릿 단의 Filter에서 처리하는 것이 적절하다 생각한다.
- Spring MVC에 의존적인 공통 로직을 처리할 때 더 유용할 것이다.
- 매핑된 핸들러 정보를 알 수 있기 때문에, 이를 이용하여 개별적인 처리가 필요하다면 좋을 것이다.
- 가독성이 더 좋다. 인터셉터의 각 함수가 어느 시점에서 수행되는 지 명확히 알 수 있다.
JWT 인증, 인가 구현 예시
- HandlerInterceptor의 prehandle 메서드를 구현하여 JWT 인증 유무에 따라 처리하였다.
- 필터 구현 방법과의 가장 큰 차이는, 매핑된 Controller 정보를 이용해서 구현할 수 있다는 것이다.
- Spring Security FilterChain을 사용하는 경우 대부분 URL 하드 코딩을 이용하지만, 해당 방법은 컨트롤러 정보를 활용하여 검증이 가능하다.
- 로그인 필요 여부를 나타내는 Custom Annotation을 구현하여, Controller에 추가하였다.
- 만약 @NoAuth가 있다면 인증 없이 수행되어도 되는 API이다.
@NoAuth @PostMapping("/sign-up") public SuccessResponse<PostUserRes> createUser(@RequestBody @Valid PostUserReq postUserReq) //...
- prehandle 구현
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean check=checkAnnotation(handler, NoAuth.class)
if(check) return true;
//...
}
private boolean checkAnnotation(Object handler,Class cls){
HandlerMethod handlerMethod=(HandlerMethod) handler;
if(handlerMethod.getMethodAnnotation(cls) !=null){
//해당 어노테이션이 존재하면 true.
return true;
}
return false;
}
- 순서
- 1) 인증 필요 유무를 확인한다.
- HandlerMethod의 getMethodAnnotation을 통해 매핑된 컨트롤러에 @NoAuth가 있는지 확인
- 인증할 필요가 없다면 별도의 추가 작업 없이 true를 반환한다.

- 2) 인증이 필요하다면, 유효한 JWT인지 검증한다.
- 만약 @NoAuth가 없다면 JWT 인증이 필요한 API이다.
- JWT가 유효하면 true, 유효하지 않다면 (만료, 잘못된 토큰) false를 반환한다.
- 만약 @NoAuth가 없다면 JWT 인증이 필요한 API이다.
'Spring' 카테고리의 다른 글
| [Spring] 불변 ResponseDTO 만들어보기 (0) | 2024.06.18 |
|---|