관심쟁이 영호

[Spring Boot] 인터셉터 ㅣ intercepter 본문

Bank-End/Spring Boot

[Spring Boot] 인터셉터 ㅣ intercepter

관심쟁이 영호 2021. 8. 3. 15:40
반응형

오늘은 스프링 부트에서 제공하는 필터와 인터셉터에 대해서 공부를 해볼 예정이다.

 

목차

  • 인터셉터란?
  • 스프링 인터셉터 인터페이스
  • 인터셉터 적용하기

인터셉터란?

하나의 예시를 통해서 이해해보자.

사용자가 Get 요청으로 로그인을 하지않은 상태에서 "회원A의 정보 수정 페이지"를 요청하면 해당 페이지가 나오지 않아야 한다.

 

그러기 위해서 인터셉터를 사용한다. 여기서 인터셉터는 해당 사용자가 "회원 A"로 로그인을 했는지 살펴봐야한다!

 

인터셉터 흐름

 

  • HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러

인터셉터는 서블릿이 컨트롤러를 호출하기 전에 호출된다는 것을 알아두자!!

 

 

인터셉터 체인

  • HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 인터셉터1 -> 인터셉터2 -> 인터셉터3 -> ... -> 컨트롤러

인터셉터는 위와 같이 여러가지의 인터셉터를 연결하여 적용할 수 있다.

 

인터셉터 제한

위의 예를 보면 인터셉터1에서 다음 인터셉터2가 적용하지 않을 수 있도록 막을 수 있다.

 


스프링 인터셉터 인터페이스

스프링 인터셉터를 사용하기 위해서는 HandlerInterceptor를 완성하면 된다.

 

public interface HandlerInterceptor {

//preHandle
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
Object handler) throws Exception {}

//postHandle
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 
@Nullable ModelAndView modelAndView) throws Exception {}

//afterCompletion
default void afterCompletion(HttpServletRequest request, HttpServletResponse response,
 Object handler, @Nullable Exception ex) throws Exception {}
 
}

스프링 인터셉터 인터페이스에는 3가지의 메소드가 존재한다.

  • preHandle
  • postHandle
  • afterCompletion

각각 호출되는 시점이 다르다.

 

호출 시점

  • preHandle : 핸들러 어댑터가 호출되기 전
  • postHandle : 컨트롤러 호출 다음인 핸들러 어댑터 호출 후
  • afterCompletion : 뷰 렌더링 이후

예외

- 정상적으로 통과가 된다면 더할 나위없이 좋겠지만, 인터셉터에서 예외가 터져버린다면 (아까와 같이 로그인이 되어있지 않은 상태에서 요청 시) 다음과 같이 호출된다.

  • preHandle : 컨트롤러 호출 전에 호출
  • postHandle: 컨트롤러에서 예외 발생시 postHandle은 호출되지 않음
  • afterCompletion : 앞에서 예외가 발생해도 발생 -> 공통 처리

인터셉터 적용하기

 

Step 1. 뷰 생성하기

- 가장먼저 로그인 상태에서만 접근할 페이지를 생성하자.

 

modify.html

<!doctype html>
<html lang="ko">
<head>
    <title>회원 수정</title>
</head>
<body>
    <form action="/modify" method="post">
        과거 아이디 : <input type="text" name="beforeId" id="beforeId"/><br>
        과거 비밀번호 : <input type="text" name="beforePassword" id="beforePassword"/><br>
        아이디 : <input type="text" name="userId" id="userId"/><br>
        비밀번호: <input type="text" name="password"/><br>
        <input type="submit" value="제출" /> <br>
    </form>
</body>
</html>

 

회원 수정 페이지는 로그인이 되어있는 사람만 접근할 수 있도록 만들 것이다.

 

이제 인터셉터를 구현해보자. 인터셉터를 구현하기 위해선 몇가지의 절차가 있다.

  1. HandlerInterceptor 구현
  2. WebConfig에 구현한 인터셉터 등록

 

Step 1. HandlerInterceptor 구현

 

ModifyInterceptor

@Slf4j
public class ModifyInterceptor implements HandlerInterceptor {

    public static final String LOG_ID = "logId";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse
            response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        String uuid = UUID.randomUUID().toString();
        request.setAttribute(LOG_ID, uuid);
        
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler; 
        }
        
        log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);
        return true; 
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse
            response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle [{}]", modelAndView);
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse
            response, Object handler, Exception ex) throws Exception {
        String requestURI = request.getRequestURI();
        String logId = (String)request.getAttribute(LOG_ID);
        log.info("RESPONSE [{}][{}]", logId, requestURI);
        if (ex != null) {
            log.error("afterCompletion error!!", ex);
        }
    }
}

 

Step 2. WebConfig에 인터셉터 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ModifyInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**", "/*.ico", "/error","/login");
    }

};

 

코드 설명 :

ModifyInterceptor에서 각각 부분에 넘어오는 정보를 log로 찍어보았다 로그는 다음과 같다.

 

- preHandle

log.info("REQUEST [{}][{}]", requestURI, handler);

REQUEST [37a7ca18-d81c-4907-b1ca-115f4dce6a11][/signup][cyh.adyb.web.SignUpContoller#signup(Model)]

사용자의 Request에 대한 정보를 가지고 들어온다.

 

- postHandle

log.info("postHandle [{}]", modelAndView);

postHandle [ModelAndView [view="/signup"; model={user=User(sequenceId=null, userId=null, password=null), org.springframework.validation.BindingResult.user=org.springframework.validation.BeanPropertyBindingResult: 0 errors}]]

PostHandle에서는 request 정보와 ModelAndView 정보를 가지고 온다.

 

- afterCompletion

log.info("RESPONSE [{}][{}]", logId, requestURI);

RESPONSE [37a7ca18-d81c-4907-b1ca-115f4dce6a11][/signup]

afterCompletion에서는 response에 대한 정보, request 정보, 해당 Handler정보, Exception에 대한 정보를 알 수 있다.

 

WebConfig

WebConfig에 인터셉터를 등록할 때, 여러가지의 속성을 정의할 수 있다.

 

- addInterceptor(new ModifyInterceptor())

ModifyInterceptor를 인터셉터로 등록한다.


- .order(1)

순서는 1번째로 설정한다.


- .addPathPatterns("/**")

해당 URL에만 인터셉터를 적용한다.


- excludePathPatterns("/css/**", "/*.ico", "/error","/login");

해당 URL은 인터셉터 처리를 제외한다.

 

추가적인 속성 설정을 할 수 있는데, 해당 사항은 문서를 확인해보자!

300x250
Comments