Spring - 인터셉터의 동작원리

지니·2023년 8월 19일
0

spring

목록 보기
5/13

이번에는 Interceptor의 동작원리를 알아보자

1. 스프링부트에서 Interceptor란?

1-1. 스프링부트에서 Interceptor의 동작원리를 이해하기 위해서는 먼저 Interceptor가 무엇인지 알아야 한다.

  • Interceptor는 스프링 MVC에서 특정한 URI 호출을 '가로채는' 역할을 한다. 이를 활용하면 기존 컨트롤러의 로직을 수정하지 않고도, 사전이나 사후에 제어가 가능하다. 즉, 요청과 응답을 가로채서 원하는 동작을 추가하는 역할을 한다.

1-2. Interceptor의 주요 동작 과정을 살펴보자

  1. 클라이언트로부터 들어오는 요청(HttpRequest)을 가로챈다.

  2. 가로챈 요청에 대해 원하는 처리(예: 로그인 체크, 권한 체크 등)를 수행한다.

  3. 처리가 끝난 후, 요청을 원래의 목적지인 컨트롤러로 전달한다.

  4. 컨트롤러가 응답(HttpResponse)을 반환하면, 이 응답 역시 가로챌 수 있다.

  5. 가로챈 응답에 대해 원하는 처리(예: 응답 로깅 등)를 수행한 후, 응답을 클라이언트로 전달한다.

  6. 이러한 동작을 통해 Interceptor는 요청과 응답 사이에 원하는 로직을 삽입할 수 있게 해준다. 이를 통해 반복적인 코드를 제거하고, 코드의 중복을 최소화하는 등의 장점을 얻을 수 있다.


2. interceptor 사용예시 (xml 사용)

  • 스프링부트에서 Interceptor를 사용하는 예시를 확인하자. 이 예시에서는 인터셉터를 사용하여 로그인 처리를 하는 방법을 보여준다.

2-1. 먼저, 인터셉터를 설정하는 방법부터 살펴보자.

  • 스프링 설정 파일에서 인터셉터를 설정할 수 있다.
<!-- Interceptors -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <bean class="com.victolee.interceptor.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>
  • 위의 설정은 모든 요청을 가로채서 com.victolee.interceptor 패키지에 있는 MyInterceptor 객체를 실행하겠다는 의미이다.

2-2. 다음으로, 인터셉터를 구현하는 방법을 살펴보자.

  • 인터셉터를 구현하기 위해서는 HandlerInterceptor 인터페이스를 구현하거나 HandlerInterceptorAdapter 클래스를 상속받아야 한다.
public class MyInterceptor implements HandlerInterceptor {

    // controller로 보내기 전에 처리하는 인터셉터
    // 반환이 false라면 controller로 요청을 안함
    // 매개변수 Object는 핸들러 정보를 의미한다. ( RequestMapping , DefaultServletHandler ) 
    @Override
    public boolean preHandle(
        HttpServletRequest request, HttpServletResponse response,
        Object obj) throws Exception {
    
        System.out.println("MyInterCeptor - preHandle");
        return false;
    }

    // controller의 handler가 끝나면 처리됨
    @Override
    public void postHandle(
        HttpServletRequest request, HttpServletResponse response,
        Object obj, ModelAndView mav)
        throws Exception {
    }

    // view까지 처리가 끝난 후에 처리됨
    @Override
    public void afterCompletion(
        HttpServletRequest request, HttpServletResponse response,
        Object obj, Exception e)
        throws Exception {
    }
}
  • 위의 코드에서 preHandle 메서드는 컨트롤러가 호출되기 전에 실행되며, postHandle 메서드는 컨트롤러가 실행된 후에 호출되고, afterCompletion 메서드는 뷰에서 최종 결과가 생성하는 일을 포함한 모든 일이 완료 되었을 때 실행된다.

2-3. 마지막으로, 로그인 처리를 위한 인터셉터를 구현하는 예제를 살펴보자

public class AuthLoginInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private UserService userService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
        Object handler) throws Exception {
    
        String email = request.getParameter("email");
        String pwd = request.getParameter("pwd");
    
        UserVO vo = new UserVO();
        vo.setEmail(email);
        vo.setPwd(pwd);
        userService.getUser(vo);
    
        return false;
    }
}
  • 위의 코드에서는 AuthLoginInterceptor가 HandlerInterceptorAdapter를 상속받아 preHandle 메서드를 오버라이딩 하여 로그인 처리를 수행한다.

  • 클라이언트가 로그인 버튼을 눌렀을 때 email과 pwd를 전달한다고 가정하면, getParameter 메서드로 데이터를 가져올 수 있고 UserVO를 Service 계층에 전달하여 해당 유저가 존재하는지 확인한다.

  • 이렇게 인터셉터를 활용하면, 로그인 처리와 같은 반복적인 작업을 컨트롤러에서 분리하여 코드의 중복을 줄이고 관리를 용이하게 할 수 있다.


3. interceptor 사용예시 (@Configuration 어노테이션 사용)

  • 이 예시에서는 @Configuration 어노테이션을 적용시킨 후 로그를 출력하는 간단한 인터셉터를 만들어보겠다.

3-1. 먼저, HandlerInterceptor 인터페이스를 구현하는 LoggerInterceptor 클래스를 만들어보자

package com.study.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class LoggerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("===============================================");
        log.debug("==================== BEGIN ====================");
        log.debug("Request URI ===> " + request.getRequestURI());
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.debug("==================== END ======================");
        log.debug("===============================================");
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
}
  • 위의 LoggerInterceptor 클래스는 HandlerInterceptor 인터페이스를 구현하고 있으며, preHandle 메서드와 postHandle 메서드를 오버라이드하고 있다.

  • preHandle 메서드는 컨트롤러가 호출되기 전에 실행되며, postHandle 메서드는 컨트롤러가 실행된 후에 호출됩니다. 이 예시에서는 각 메서드에서 로그를 출력하도록 구현하였다.

3-2. 다음으로, WebMvcConfigurer 인터페이스를 구현하는 WebMvcConfig 클래스를 만들어보자. 이 클래스에서 인터셉터를 등록한다.

package com.study.config;

import com.study.interceptor.LoggerInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggerInterceptor())
                .excludePathPatterns("/css/**", "/images/**", "/js/**");
    }
}
  • 위의 WebMvcConfig 클래스는 WebMvcConfigurer 인터페이스를 구현하고 있으며, addInterceptors 메서드를 오버라이드하고 있다. addInterceptors 메서드에서는 LoggerInterceptor를 등록하고, 특정 패턴의 경로를 인터셉터의 적용에서 제외하고 있다.

  • 이렇게 설정하면, 인터셉터가 정상적으로 동작하게 된다. 이 예시에서는 모든 요청에 대해 요청 URI를 로그로 출력하게 된다. 참고로, 이 예시는 스프링 부트 2.x 버전을 기준으로 작성되었다. 스프링 부트 3에서도 동일하게 적용할 수 있다.

profile
탐구하는 Backend 개발자

0개의 댓글

관련 채용 정보