๐Ÿ“‚ ์Šคํ”„๋ง/๊ธฐ๋ณธ ๊ฐœ๋…

์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ(Spring Interceptor)

Amenable 2023. 4. 30. 00:49

  ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์™€ ๊ฐ™์ด ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ(Spring Interceptor) ๋˜ํ•œ ์›น๊ณผ ๊ด€๋ จ๋œ ๊ณตํ†ต ๊ด€์‹ฌ ์‚ฌํ•ญ์„ ํšจ๊ณผ์ ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ˆ ์ด๋‹ค.

  ์ด๋ฒˆ ๊ธ€์„ ํ†ตํ•ด '์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ์˜ ๊ฐœ๋…'์„ ์•Œ์•„๋ณด๊ณ , '์š”์ฒญ ๋กœ๊ทธ ์˜ˆ์ œ'์™€ '์ธ์ฆ ์ฒดํฌ ์˜ˆ์ œ'๋ฅผ ํ†ตํ•ด์„œ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

 

1. ๊ฐœ๋… ๐ŸŒ 

๐Ÿ“˜ 1. ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ ํ๋ฆ„

  ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ๋ฆ„์„ ๊ฐ€์ง„๋‹ค.

HTTP ์š”์ฒญ โ†’ WAS โ†’ ํ•„ํ„ฐ โ†’ ์„œ๋ธ”๋ฆฟ(Dispatcher Servlet) โ†’ ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ โ†’ ์ปจํŠธ๋กค๋Ÿฌ

  ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ๋Š” ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ๊ณผ ์ปจํŠธ๋กค๋Ÿฌ ์‚ฌ์ด์—์„œ ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ง์ „์— ํ˜ธ์ถœ๋œ๋‹ค. 

๐Ÿ“˜ 2. ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ ์ œํ•œ

  ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์™€ ๊ฐ™์ด ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž์˜ ์ ‘๊ทผ๊ณผ ๊ฐ™์€ '์ ์ ˆํ•˜์ง€ ์•Š์€ ์š”์ฒญ'์— ๋Œ€ํ•˜์—ฌ ํŒ๋‹จํ•˜๊ณ  ๊ฑฐ๊ธฐ์—์„œ ๋์„ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

(1) ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์ธ ๊ฒฝ์šฐ
HTTP ์š”์ฒญ โ†’ WAS โ†’ ํ•„ํ„ฐ โ†’ ์„œ๋ธ”๋ฆฟ โ†’ ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ โ†’ ์ปจํŠธ๋กค๋Ÿฌ
(2) ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž์ธ ๊ฒฝ์šฐ
HTTP ์š”์ฒญ โ†’ WAS โ†’ ํ•„ํ„ฐ โ†’ ์„œ๋ธ”๋ฆฟ โ†’ ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ

๐Ÿ“˜ 3. ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ ์ฒด์ธ

  ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์ฒด์ธ์œผ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค

HTTP ์š”์ฒญ โ†’ WAS โ†’ ํ•„ํ„ฐ โ†’ ์„œ๋ธ”๋ฆฟ โ†’ ์ธํ„ฐ์…‰ํ„ฐ1 โ†’ ์ธํ„ฐ์…‰ํ„ฐ2 โ†’ ์ปจํŠธ๋กค๋Ÿฌ

  ๊ทธ๋ž˜์„œ ์—ฌ๋Ÿฌ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๋Š” ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๋จผ์ € ์ ์šฉํ•˜๊ณ , ๊ทธ๋ฆฌ๊ณ  ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•˜๋Š” ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“˜ 4. ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค

public interface HandlerInterceptor {
    
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}
    
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}
    
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}   
}

  ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด HandlerInterceptor ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

  ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์˜ ๊ฒฝ์šฐ doFilter()๋กœ๋งŒ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ, ์ธํ„ฐ์…‰ํ„ฐ๋Š” preHandle(์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ „), postHandle(์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ํ›„), afterCompletion(์š”์ฒญ ์™„๋ฃŒ ์ดํ›„)์™€ ๊ฐ™์ด ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์—์„œ ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • preHandle
    ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ „์— ํ˜ธ์ถœ๋œ๋‹ค. (์ •ํ™•ํžˆ๋Š” ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ํ˜ธ์ถœ๋˜๊ธฐ ์ „์— ํ˜ธ์ถœ๋œ๋‹ค.)
    preHandle์˜ ์‘๋‹ต๊ฐ’์ด true์ด๋ฉด ๋‹ค์Œ์„ ์ง„ํ–‰ํ•˜๊ณ , false์ด๋ฉด ์ง„ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. (false์ด๋ฉด 1๋ฒˆ ๊ณผ์ •์—์„œ ๋์ด ๋‚œ๋‹ค.)
  • postHandle
    ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ํ›„์— ํ˜ธ์ถœ๋œ๋‹ค. (์ •ํ™•ํžˆ๋Š” ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ํ˜ธ์ถœ ํ›„์— ํ˜ธ์ถœ๋œ๋‹ค.)
  • afterCompletion 
    ๋ทฐ๊ฐ€ ๋žœ๋”๋ง ๋œ ์ดํ›„์— ํ˜ธ์ถœ๋œ๋‹ค.

  ๋˜ํ•œ, 'Object handler'๋ฅผ ์ด์šฉํ•˜์—ฌ ์–ด๋–ค ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ๋˜๋Š”์ง€ ํ˜ธ์ถœ์ •๋ณด๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ณ , 'ModelAndView modelAndView'๋ฅผ ์ด์šฉํ•˜์—ฌ ์–ด๋–ค ๋‚ด์šฉ๋“ค์ด ๋ฐ˜ํ™˜๋˜๋Š”์ง€ ์‘๋‹ต ์ •๋ณด๋„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“˜ 5. ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ƒํ™ฉ์—์„œ์˜ ํ๋ฆ„

  ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ์•„๋ž˜์™€ ๊ฐ™์ด ์ง„ํ–‰๋œ๋‹ค.

  • preHandle
    ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ „์— ํ˜ธ์ถœ๋œ๋‹ค.
  • postHandle
    ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด postHandle์€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • afterCompletion
    ํ•ญ์ƒ ํ˜ธ์ถœ๋œ๋‹ค.
    ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์˜ˆ์™ธ(ex)๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์„œ ์–ด๋–ค ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ๋กœ๊ทธ๋กœ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.
    ์˜ˆ์™ธ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ณตํ†ต ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ ค๋ฉด postHandle์ด ์•„๋‹ˆ๋ผ afterCompletion์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

2. ์š”์ฒญ ๋กœ๊ทธ ์˜ˆ์ œ ๐ŸŒ€

@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(); // HTTP ์š”์ฒญ์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ
        request.setAttribute(LOG_ID, uuid);

        if(handler instanceof HandlerMethod){
            HandlerMethod hm = (HandlerMethod) handler; // ํ˜ธ์ถœํ•  ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ ์žˆ๋‹ค.
        }

        log.info("REQUEST [{}][{}][{}]", uuid, requestURI, handler);
        return true; // false์ธ ๊ฒฝ์šฐ ๋” ์ด์ƒ ์ง„ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
    }

    @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);
        }
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
        registry.addInterceptor(new LogInterceptor()) // ์ธํ„ฐ์…‰ํ„ฐ ๋“ฑ๋ก
                .order(1) // ํ˜ธ์ถœ์ˆœ์„œ ์ง€์ •
                .addPathPatterns("/**") // ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ ์šฉํ•  URL ํŒจํ„ด ์ง€์ •
                .excludePathPatterns("/css/**", "/*.ico", "/error"); // ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ์ œ์™ธํ•  ํŒจํ„ด ์ง€์ •
    }
}

  WebMvcConfigurer๊ฐ€ ์ œ๊ณตํ•˜๋Š” addInterceptors()๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.

  addInterceptor()๋ฅผ ์ด์šฉํ•ด์„œ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๋“ฑ๋กํ•˜๊ณ , order()๋ฅผ ์ด์šฉํ•ด์„œ ํ˜ธ์ถœ ์ˆœ์„œ๋ฅผ ์ง€์ •ํ•˜๊ณ , addPathPatterns()๋ฅผ ์ด์šฉํ•ด์„œ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ ์šฉํ•  URL ํŒจํ„ด์„ ์ง€์ •ํ•˜๊ณ , excludePathPatterns()๋ฅผ ์ง€์ •ํ•ด์„œ ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ์ œ์™ธํ•  ํŒจํ„ด์„ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

  ์‹คํ–‰์‹œ์ผœ ๋ณด๋ฉด ๋กœ๊ทธ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.

2023-04-30T00:18:11.301+09:00 INFO 22272 --- [nio-8080-exec-1] com.interceptor.LogInterceptor : REQUEST [18844889-7f8f-4b7f-8848-1b04472c65bb][/][com.interceptor.MainController#index(Model)]
2023-04-30T00:18:11.314+09:00 INFO 22272 --- [nio-8080-exec-1] com.interceptor.LogInterceptor : postHandle [ModelAndView [view="home"; model={}]]
2023-04-30T00:18:11.665+09:00 INFO 22272 --- [nio-8080-exec-1] com.interceptor.LogInterceptor : RESPONSE [18844889-7f8f-4b7f-8848-1b04472c65bb][/]

 

3. ์ธ์ฆ ์ฒดํฌ ์˜ˆ์ œ ๐ŸŒ

@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();

        // ์ธ์ฆ์ด ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ
        if(...) {
            log.info("๋ฏธ์ธ์ฆ ์‚ฌ์šฉ์ž ์š”์ฒญ");

            // ๋กœ๊ทธ์ธ์œผ๋กœ redirect
            response.sendRedirect("/login?redirectURL="+requestURI);
            return false;
        }

        return true;
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
        registry.addInterceptor(new LogInterceptor()) 
                ...

        registry.addInterceptor(new LoginCheckInterceptor()) // ์ธํ„ฐ์…‰ํ„ฐ ๋“ฑ๋ก
                .order(2) // ํ˜ธ์ถœ์ˆœ์„œ ์ง€์ •
                .addPathPatterns("/**") // ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ ์šฉํ•  URL ํŒจํ„ด ์ง€์ •
                .excludePathPatterns("/", "/members/add", "/login", "/logout",
                        "/css/**", "/*.ico", "/error"); // ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ์ œ์™ธํ•  ํŒจํ„ด ์ง€์ •
    }
}

  'ํ™ˆ, ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ, ๋ฆฌ์†Œ์Šค ์กฐํšŒ, ์˜ค๋ฅ˜'์™€ ๊ฐ™์€ ๋ถ€๋ถ„์€ ๋กœ๊ทธ์ธ ์ฒดํฌ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š๋„๋ก ํ•˜์˜€๋‹ค. 

  ๊ทธ๋ฆฌ๊ณ  ๋กœ๊ทธ์ธ์„ ํ•œ ์ดํ›„ ๋‹ค์‹œ ์›๋ž˜ ํŽ˜์ด์ง€๋กœ ๋Œ์•„์˜ค๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ redirectURL์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ถ™์—ฌ์ฃผ์—ˆ๋‹ค.

 

ํ•ด๋‹น ๊ธ€์€ ๊น€์˜ํ•œ ๋‹˜์˜ '์Šคํ”„๋ง MVC 2ํŽธ - ๋ฐฑ์—”๋“œ ์›น ๊ฐœ๋ฐœ ํ™œ์šฉ ๊ธฐ์ˆ '์„ ์ฐธ๊ณ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.