> For the complete documentation index, see [llms.txt](https://docs.sysout.co.kr/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sysout.co.kr/web/back-end/spring-boot/spring-aop/interceptor.md).

# Interceptor

## 스프링 인터셉터

스프링 인터셉터(Spring Interceptor)란 요청이 처리되는 시점들을 간섭할 수 있도록 만들어진 AOP 객체를 말한다. 다음 클래스들을 제공한다.

* Interface `HandlerInterceptor`
* Class `HandlerInterceptorAdapter` (비추천)

![](/files/-MdFkD6lIdFaLsDrBAQK)

간섭이란 해당 시점의 진행 상황을 살펴보거나 변조할 수 있도록 가로채는 것을 말한다. 인터셉터를 통해 간섭할 수 있는 시점은 다음과 같다.

* `preHandle` : 요청이 핸들링(연결)되기 전. 즉, 컨트롤러 실행 전
* `postHandle` : 요청이 핸들링(연결)된 후. 즉, 컨트롤러 실행 후
* `afterCompletion` : 요청에 대한 처리가 최종적으로 완료된 후. 즉, View 생성 후
* ~~`afterConcurrentHandlingStarted` : Servlet 3.0부터 가능한 `비동기 요청 처리` 방식의 경우 `postHandle`, `afterCompletion` 대신 실행 (비추천)~~

## 인터셉터 클래스 작성 및 등록

### 인터셉터 클래스 생성

인터셉터는 다음과 같이 생성한다.

```java
@Slf4j
@Component
public class TestInterceptor extends HandlerInterceptorAdapter{

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{ 
        log.info("prehandle = {}", handler);
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle = {}, {}", handler, modelAndView);
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
        log.info("afterCompletion = {}, {}", handler, ex);
    }
    
}
```

### 인터셉터 등록

Spring boot에서는 인터셉터를 <mark style="color:blue;">`WebMvcConfigurer`</mark> 클래스를 상속 받은 설정 클래스에 구현한다. <mark style="color:blue;">`WebMvcConfigurer`</mark> 인터페이스를 상속받은 뒤 <mark style="color:red;">`@Configuration`</mark> 으로 설정 클래스임을 명시하면 제공되는 메소드를 통해 운영 설정을 수행할 수 있다.

```java
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry){
    
    }
}
```

생성된 인터셉터에 등록한 인터셉터를 주입한다.

```java
@Autowired
private TestInterceptor testInterceptor;
```

주입된 인터셉터를 재정의한 설정 메소드에 추가한다.

```java
registry.addInterceptors(testInterceptor).addPathPatterns("/**");
```

완성된 코드는 다음과 같다.

<details>

<summary>InterceptorConfiguration</summary>

```java
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {
    
    @Autowired
    private TestInterceptor testInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(testInterceptor).addPathPattern("/**");
    }
    
}
```

</details>

제공되는 매개변수 <mark style="color:blue;">`InterceptorRegistry`</mark>에는 인터셉터와 관련된 정보, 기능들이 존재하며 이 중 **addInterceptor** 메소드를 이용하여 인터셉터를 추가한다. 또한 **addPathPattern** 메소드와 **excludePathPattern** 메소드를 통해 간섭할 대상 매핑을 설정하거나 제외시킬 수 있다.

## Mapping url 작성규칙

Interceptor의 Mapping을 작성할 때에는 다음의 내용을 유의해야 한다.

* 와일드카드(\*)를 사용할 수 있다.
* 와일드카드(\*)의 개수가 1개인지 2개인지에 따라 의미가 달라진다.
  * 1개인 경우는 현재 endpoint 내에서 어떤 글자가 와도 상관없다는 의미이다.
  * 2개인 경우는 현재 endpoint와 하위 전체에서 어떤 글자가 와도 상관없다는 의미이다.

### 매핑 작성 예시(1)

```java
registry.addInterceptors(...).addPathPatterns("/member*");
```

위와 같이 path를 `/member*`로 지정하면 엔드포인트(endpoint)는 `/`로 설정된다.\
따라서 다음 주소들이 매핑된다.

* /member
* /member\_info
* /memberlist

하위 엔드포인트 주소는 포함되지 않는다.&#x20;

* /member/info
* /member/regist
* /member/list

### 매핑 작성 예시(2)

```java
registry.addInterceptors(...).addPathPatterns("/member/*");
```

위와 같이 path를 `/member/*`로 지정하면 엔드포인트(endpoint)는 `/member/`로 설정된다.\
따라서 다음 주소들이 매핑된다.

* /member/info
* /member/regist
* /member/list

다음 주소들은 매핑되지 않는다.

* /member
* /member\_info
* /memberlist
* /member/list/1
* /member/search/module

### 매핑 작성 예시(3)

```java
registry.addInterceptors(...).addPathPatterns("/member/*");
```

위와 같이 path를 `/member/**`로 지정하면 엔드포인트(end-point)는 `/member/`로 설정된다.\
`**`는 `/`를 포함할 수 있으므로 다음 주소들이 매핑된다.

* /member/info
* /member/regist
* /member/list
* /member/list/1
* /member/find/id/test

다음 주소들은 매핑되지 않는다.

* /member
* /member\_info
* /memberlist

## 인터셉터 메소드 분석

인터셉터의 각각 메소드를 분석하여 어떠한 행위를 할 수 있는지 파악해본다.

### preHandle

* 반환 형태
  * `boolean` : 요청의 통과(continue) 여부를 반환할 수 있다. `true` 반환 시 통과 , `false` 반환 시 차단된다.
* 매개 변수
  * `HttpServletRequest` : 요청 정보를 가지고 있는 객체이다.
  * `HttpServletResponse` : 응답 정보를 가지고 있는 객체이다.
  * `Object` : 실행될 대상의 정보를 가지고 있는 객체이다.(일반적으로 컨트롤러의 매핑 메소드)

### postHandle

* 반환 형태
  * `void` : 이 메소드는 어떠한 결과도 전달할 수 없다.
* 매개 변수
  * `HttpServletRequest` : 요청 정보를 가지고 있는 객체이다.
  * `HttpServletResponse` : 응답 정보를 가지고 있는 객체이다.
  * `Object` : 실행된 대상의 정보를 가지고 있는 객체이다.(일반적으로 컨트롤러의 매핑 메소드)
  * `ModelAndView` : 처리 완료 후의 `Model`과 `View`의 정보를 가진 객체이다.

### afterCompletion

* 반환 형태
  * `void` : 이 메소드는 어떠한 결과도 전달할 수 없다.
* 매개 변수
  * `HttpServletRequest` : 요청 정보를 가지고 있는 객체이다.
  * `HttpServletResponse` : 응답 정보를 가지고 있는 객체이다.
  * `Object` : 실행된 대상의 정보를 가지고 있는 객체이다.(일반적으로 컨트롤러의 매핑 메소드)
  * `Exception` : 페이지 렌더링 시 발생한 예외 정보를 가진 객체이다.

### afterConcurrentHandlingStarted

<mark style="color:blue;">`AsyncHandlerInterceptor`</mark>를 이용하여 사용할 것을 권장한다.

## Object Handler의 활용

Handler를 이용하면 다음 작업들을 처리할 수 있다.

* 실행될(또는 실행된) 대상의 `유형` 확인
* 실행될(또는 실행된) 대상의 `Annotation Type` 확인
* 그 외 해당 대상의 정보 분석(매개변수, 반환형, 이름, 등...)

> Object Handler를 이용하여 대상의 Annotation 정보 읽어오는 코드

```java
if(handler instanceof HandlerMethod){
    HandlerMethod hm = (HandlerMethod) handler;
    log.info("method name = {}", hm.getMethod().getName());
    log.info("method annotation = {}", hm.getMethodAnnotation(RequestMapping.class));
    //...(이후 원하는 조건 등을 구현)
}
```

이를 응용하면 `Custom Annotation`을 만들어두고 이를 검사하여 원하는 작업을 처리하도록 하는 등의 작업을 구현할 수 있다.
