easycode

[Spring] 스프링의 인터셉터(Interceptor) 본문

Spring

[Spring] 스프링의 인터셉터(Interceptor)

ez() 2024. 1. 21. 22:30


Interceptor란?

  • '낚아채다'라는 의미
  • 컨트롤러의 핸들러(Handler)를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 일종의 필터
  • 사용자 요청에 의해 서버에 들어온 request 객체를 컨트롤러의 핸들러(사용자가 요청한 api에 따라 실행되어야 할 메서드, 이하 핸들러)로 도달하기 전에 낚아채서 개발자가 원하는 추가적인 작업을 한 후 핸들러로 보낼 수 있도록 해주는 것

 

 

어떤 때에 사용하는가?

  • 특정 컨트롤러의 핸들러가 실행되기 전후에 추가적인 작업을 원할 때 사용
    • 여러 컨트롤러에서 공통으로 처리해야 하는 작업을 중앙에서 관리할 수 있다. 예를 들어 인증이나 권한 검사, 로깅등과 같은 전역적인 작업을 인터셉터에서 처리할 수 있다. 이는 코드의 중복을 줄이고 유지보수성을 향상하는데 도움을 준다.
    • 요청 처리 제어 : 인터셉터는 HTTP 요청 전후의 처리 흐름을 제어할 수 있습니다. 클라이언트의 요청이 컨트롤러에 도달하기 전에 사전 작업을 수행하거나, 컨트롤러의 실행 이후에 사후 작업을 수행하는 등의 제어가 가능합니다. 이를 통해 요청의 전처리나 후처리를 담당할 수 있습니다.
    • `HandlerMapping`이 결정한 handler을 `HandlerAdapter` 수행 전, 후로 가로체어 추가적인 작업이 가능하다.
  • 권한 체크를 바탕으로 자세한 예시를 들자면?
    • 관리자 계정만이 실행할수 있는 Controller 핸들러를 작성한다고 했을 때, 오직 관리자 계정만 실행할 수 있도록 하기 위해 핸들러에 접근하는 사용자가 관리자인지 확인하는 세션 체크 코드를 각 핸들러에 작성해줘야 한다. 핸들러수가 적다면 괜찮겠지만, 적용해야 할 핸들러가 수천 개라면...?
    • 크게 두 가지 문제가 발생한다.
      1) 메모리 낭비, 서버의 부하가 늘어난다. 적용해야할 핸들러만 수만큼 세션체크 코드를 작성함으로써 반복되는 코드들이 매우 많아지기 때문이다.
      2) 코드 누락으로 인해 해당 코드를 미작성한 핸들러가 생길 수 있다.
    • 만약 회원정보에 접근하는 핸들러에 세션 체크 코드가 누락되어 자격이 없는 사용자가 접근하게 된다면 보안적으로 매우 큰일이다!!! 그래서 인터셉터로 이런 상황을 방지해 주는 것이다.
  • Interceptor를 사용하게 되면 핸들러 수 만큼 작성했던 세션 체크 코드를 인터셉터 클래스에 한 번만 작성하면 된다. 이로 인해 코드의 양이 줄어 메모리 낭비도 줄일 수 있다. 
  • 인터셉터 적용 유무의 기준이 되는 url을 servlet-context.xml에 설정해 주면 스프링에서 일괄적으로 해당 url 경로의 핸들러에 인터셉터를 적용해 주기 때문에 누락에 대한 위험이 줄어든다.

 


Interceptor 동작 위치 및 순서

Interceptor는 Spring Container 내에서 Handler Mapping(수행할 handler 결정) 이후 HandlerAdaptor(결정된 핸들러 수행) 전/후 과정에서 수행된다.
  1. 사용자(클라이언트)가 서버에 api로 요청을 보낸다.
  2. DispatcherServlet이 해당 Request 객체를 받아 분석한 뒤, 'Handler Mapping'에게 사용자 요청을 처리할 핸들러를 찾도록 요청한다.
  3. 그 결과로 핸들러 실행체인(HandlerExectuonChanin)이 동작하게 되는데, 이 핸들러 실행체인은 하나 이상의 핸들러 인터셉터를 거쳐 컨트롤러가 실행 될 수 있도록 구성되어 있다.
    1. 만약 핸들러 인터셉터를 등록하지 않았다면, 곧바로 컨트롤러가 실행된다. 반대로 하나 이상의 인터셉터가 지정되어 있다면 지정된 순서에 따라 인터셉터를 거쳐 컨트롤러를 실행한다.

 

 

메서드의 종류

1. preHandle()

  • 컨트롤러가 호출되기 전에 실행
  • 컨트롤러 실행 이전에 처리해야 할 작업이 있는 경우 or 요청정보를 가공하거나 추가하는 경우 사용
  • 실행되어야 할 '핸들러'에 대한 정보를 인자값으로 받기 때문에 '서블릿 필터'에 비해 세밀하게 로직을 구성할수 있다.
  • 리턴값이 boolean이다. 리턴이 true일 경우 preHandle() 실행 후 핸들러에 접근한다. false일경우 작업을 중단하기 때문에 컨트롤러와 남은 인터셉터가 실행되지 않는다.

2. postHandle()

  • 핸들러가 실행은 완료 되었지만, 아직 View가 생성되기 이전에 호출된다.
  • ModelAndView 타입의 정보가 인자값으로 받는다. 따라서 Controller에서 View 정보를 전달하기 위해 작업한 Model 객체의 정보를 참조하거나 조작할 수 있다.
  • preHandle() 에서 리턴값이 fasle인 경우 실행되지 않는다.
  • 적용 중인 인터셉터가 여러 개 인경우, preHandle()는 역순으로 호출된다.
  • 비동기적 요청처리 시에는 처리되지 않는다.

3. afterCompletion()

  • 모든 View에서 최종 결과를 생성하는 일을 포함한 모든 작업이 완료된 후에 실행된다.
  • preHandle() 에서 리턴값이 false인 경우 실행되지 않는다.
  • 적용 중인 인터셉터가 여러 개인 경우 preHandle()는 역순으로 호출된다.
  • 비동기적 요청 처리시에 호출되지 않는다.

 

 

어떻게 구현하는가?

1. org.springframework.web.servlet.HandlerInterceptor 인터페이스를 구현

2. org.springframework.web.servlet.handler.HandlerInterceptorAdapter 추상클래스를 오버라이딩

3. mybatis의 경우 ibatis 패키지의 Interceptor 인터페이스를 구현하고 @Intercepts, @Signature 어노테이션을 붙여준다.

 


참고 사이트

1. https://popo015.tistory.com/115

2. https://velog.io/@dnflekf2748/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9D%B8%ED%84%B0%EC%85%89%ED%84%B0Interceptor

3. [https://livenow14.tistory.com/61](https://livenow14.tistory.com/61) [경험의 연장선:티스토리]