Spring

[Spring MVC] Spring Interceptor에 대해 알아보자

Too Long Subquery 2023. 7. 26. 13:14

인터셉터란? 

  • 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과의 의존성과 별개로, 보안 관련된 작업을 인터셉터 내부에서까지 끌고 올 필요는 없다 생각한다. 보안적으로 유효하지 않은 클라이언트의 요청은 최대한 빠르게 걸러내는 것이 보안적으로 좋을 것이다.
  • 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를 반환한다.

'Spring' 카테고리의 다른 글

[Spring] 불변 ResponseDTO 만들어보기  (0) 2024.06.18