로그인 처리 - 인터셉터

byeol·2023년 3월 29일
0

인터셉터에 대해서 정리하려고 합니다.
배우면서 생기는 궁금증들을 바탕으로 작성하려고 노력했습니다.


구조 분석

마찬가지로 수업을 배울 때 항상 실무자들은 어떻게 구조를 만드는지
눈여겨 보려고 노력합니다. 오늘 배운 부분도 구조를 분석합니다.

이번 수업에서는 Filter와 interceptor에 대해서 배웠고 둘은 web 폴더 아래 생성되어져 있습니다.
또한 ArgumentResolver 활용에 대해서 배웠습니다.


스프링 인터셉터

사실 여기서 들었던 의문점은 Filter를 Bean으로 등록한다고 했는데
교재에는 서블릿은 서블릿이 제공하는 기술이고 스프링 인터셉터는 스프링 MVC가 제공하는 기술이라고 한다.

어떻게 서블릿에서 제공하는 기술이 스프링 컨테이너에 빈으로 등록될 수 있는걸까?

나와 같은 의문을 가진 분이 계셨고
아주 친절하게 그 이유를 설명해두셨다.

DelegatingFilterProxy의 등장으로 가능하게 된것이다!
원래는 불가능했던 일이었다고 한다.
필터에서도 DI와 같은 스프링 기술이 필요했고 스프링 빈으로 주입받을 수 있는 대안으로 나온 것이 DelegatingFilterProxy

동작원리

    1. Filter 구현체가 스프링 빈으로 등록됨
    1. ServletContext가 Filter 구현체를 갖는 DelegatingFilterProxy를 생성함.
    1. ServletContext가 DelegatingFilterProxy를 서블릿 컨테이너 필터로 등록함
    1. 요청이 오면 DelegatingFilterProxy가 필터 구현체에게 요청을 우위임하여 필터 처리를 진행함
      (출처 : https://mangkyu.tistory.com/221)

다시 돌아와서

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

필터와 다르게 매우 정밀하게 URL 패턴을 설정할 수 있다.

스프링 인터셉터 제한
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 -> 컨트롤러
HTTP 요청 -> WAS -> 필터 -> 서블릿 -> 스프링 인터셉터 // 비로그인

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

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

스프링 공식 문서에 따르면

Workflow interface that allows for customized handler execution chains. Applications can register any number of existing or custom interceptors for certain groups of handlers, to add common preprocessing behavior without needing to modify each handler implementation.

" 사용자 정의된 처리기 실행 체인을 허용하는 워크플로 인터페이스. 응용 프로그램은 특정 핸들러 그룹에 대해 기존 또는 사용자 지정 인터셉트를 수에 상관없이 등록하여 각 핸들러 구현을 수정할 필요 없이 공통 전처리 동작을 추가할 수 있습니다. "라고 파파고가 해석해줬다.

3가지의 메서드로 구성되어 있다.

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    핸들러 호출 전에 실행된다. (핸들러 = 컨트롤러)
    Interception point before the execution of a handler.
  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
    핸틀러의 완벽한 실행 후에 실행된다.
    Interception point after successful execution of a handler.
  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
    요청 처리 완료 후 즉 뷰 렌더링 후에 콜백한다.
  • 매개변수를 보면 Object handler : 어떤 컨트롤러 호출되는지
  • ModelAndView modelAndView : 어떤 뷰가 반환되는지

결국은 스프링 MVC에 특화된 기술이라는 것을 확인할 수 있다.
따라서 필터를 사용해야 하는 경우가 아니라면 인터셉터를 사용할것!

HandlerInteceptor를 구현한 요청 로그 인터셉터

@Slf4j
public class LogInterceptor 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);
        }

    }

}

여기서 눈여겨 보아할 부분은 호출 시점이 분리된 인터셉터의 3가지 메소드들이 값을 공유하기 위해서 선택한 방법이 request.setAttribute(LOG_ID, uuid))라는 것이다.

WebConfig에 인터셉터 등록

@Configuration
public class WebConfig implements WebMvcConfigurer {
   @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**","/*.ico","error");
    }
    ...
}

등록하는 방법

  • WebMvcConfigurer가 제공하는 addInterceptors()를 사용해서 인터셉터를 등록
  • order(1) : 인터셉터의 호출 순서를 지정
  • addPathPatterns("/**") : 인터셉터를 적용한 URL 패턴을 지정
  • excludePathPatterns("/css/**","/*.ico","/error") : 인터셉터에서 제외할 패턴 지정
  • 설정 클래스에서 메서드 지정해서 구체적으로 설정 가능

WebMvcConfigurer에 대해 궁금해서 찾아보니
Spring MVC에서 아주 유용하게 사용되는 기능들이 선언
모든 메서드들이 default로 선언되어 있어 반드시 재정의할 필요없이 필요할 때만 재정의
그 중에서 예로 들 것은 addViewContollers이다.
스프링은 간단한 페이지를 보여주려해도 Controller가 필요하다.
addViewControllers는 Controller를 거치지 않고 바로 페이지를 보여준다.

//스프링
@GetMapping("/login")
public String loginForm(Model model){
   return "login";
}
//WebMvcController
public class MVCConfig implements WebMvcConfigurer {
  @Override
  public void addViewControllers(ViewControllerRestry registry){
    registry.addViewController("/login").setViewName("login");
  }
}

출처: https://goodteacher.tistory.com/258


HandlerInteceptor를 구현한 인증 체크 인터셉터

@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {

        String requestURI = request.getRequestURI();

        log.info("인증 체크 인터셉터 실행 {}", requestURI);
        HttpSession session = request.getSession(false);

        if(session == null || session.getAttribute(SessionConst.LOGIN_MEMBER)==null){
            log.info("미인증 사용자 요청");
            response.sendRedirect("/login?redirectURL="+requestURI);
            return false;
        }
        return true;
    }
}

WebConfig에 인터셉터 등록

  @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/css/**","/*.ico","error");

        registry.addInterceptor(new LoginCheckInterceptor())
                .order(2)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/","/members/add","/login","/logout",
                        "/css/**","/*.ico","/error"
                );
    }

두 번째로 접근해야 하므로 order(2)

정리

서블릿 필터와 스프링 인터셉터는 웹과 관련된 공통 관심 해결하기 위한 기술.
스프링 인터셉터가 더 편리하므로 특별한 문제가 없으면 인터셉터 사용!

profile
꾸준하게 Ready, Set, Go!

0개의 댓글

관련 채용 정보