์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ž

์ธํ„ฐ์…‰ํ„ฐ๋ž€?

ํ•„ํ„ฐ์™€ ๋™์ผํ•˜๊ฒŒ ๊ณตํ†ต ๊ด€์‹ฌ ์‚ฌํ•ญ์„ ํšจ๊ณผ์ ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ์ˆ ์ด๋‹ค. ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์„œ๋ธ”๋ฆฟ์ด ์ œ๊ณตํ•˜๋Š” ํ•„ํ„ฐ์™€ ๋‹ค๋ฅด๊ฒŒ ์Šคํ”„๋ง MVC์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ์ˆ ์ด๋ฉฐ ํ•„ํ„ฐ๋ณด๋‹ค ์ข€ ๋” ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค. ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์Šคํ”„๋ง MVC๊ฐ€ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋””์ŠคํŒจ์ณ ์„œ๋ธ”๋ฆฟ ์ดํ›„์— ํ˜ธ์ถœ๋˜๊ฒŒ ๋œ๋‹ค. ์Šคํ”„๋ง MVC์˜ ์‹œ์ž‘์ ์ด ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. 

 

์•„ํ‚คํ…์ฒ˜ 

 

ํ•„ํ„ฐ VS ์ธํ„ฐ์…‰ํ„ฐ 

ํฐ ํ‹€์—๋Š” ์ฐจ์ด๊ฐ€ ์—†๋‹ค.

์šฐ์„  ํ•„ํ„ฐ์™€ ์ธํ„ฐ์…‰ํ„ฐ์˜ ์ „์ฒด์ ์ธ ๊ธฐ๋Šฅ์—๋Š” ํฐ ์ฐจ์ด๊ฐ€ ์—†๋‹ค. ํ•„ํ„ฐ ์ธํ„ฐ์…‰ํ„ฐ ๋‘˜๋‹ค ์š”์ฒญ์— ๋Œ€ํ•œ ์ „ํ›„ ์ฒ˜๋ฆฌ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋˜ํ•œ URL Pattern์„ ์ด์šฉํ•˜์—ฌ ์–ด๋– ํ•œ ์š”์ฒญ์— ๋Œ€ํ•ด ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•  ๊ฒƒ์ธ์ง€ ์ง€์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋˜ํ•œ request, response ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์„œ ๋ถ€๊ฐ€์ ์ธ ๋กœ์ง ๋˜ํ•œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค. 

 

์ž์„ธํžˆ ๋ณด์•„์•ผ ์ฐจ์ด์ ์ด ๋ณด์ธ๋‹ค.

์ „์ฒด์ ์ธ ๊ธฐ๋Šฅ์„ ๋ณด๋ฉด ๊ฐ™์ง€๋งŒ ์ž์„ธํžˆ ๋“ค์—ฌ๋‹ค๋ณด๋ฉด ํ™•์‹คํžˆ ์ฐจ์ด๊ฐ€ ์กด์žฌํ•œ๋‹ค.  ๋‹ค์Œ์€ ๋‚˜๋งŒ์˜ ์˜ท์žฅ ํ”„๋กœ์ ํŠธ์—์„œ ๊ตฌํ˜„ํ•œ ๊ฐ๊ฐ ๋กœ๊ทธ ์ธํ„ฐ์…‰ํ„ฐ์™€ ๋กœ๊ทธ ํ•„ํ„ฐ์˜ ๋ฉ”์ธ ๊ตฌํ˜„๋ถ€์ด๋‹ค. 

 

๋กœ๊ทธ ํ•„ํ„ฐ 

@Slf4j
public class LogFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("init");
    }

    @Override
    public void destroy() {
        log.info("destroy");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("doFilter");

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();
        String method = httpRequest.getMethod();
        String uuid = UUID.randomUUID().toString();

        try {
            log.info("REQUEST [{}][{}: {}]", uuid, method, requestURI);
            chain.doFilter(request, response); // ๋‹ค์Œ ํ•„ํ„ฐ ํ˜ธ์ถœ
        } catch (Exception e) {
            throw e;
        } finally {
            log.info("RESPONSE [{}][{}: {}]", uuid, method, requestURI);
        }
    }
}

 

๋กœ๊ทธ ์ธํ„ฐ์…‰ํ„ฐ 

@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 method = request.getMethod();
        String uuid = UUID.randomUUID().toString();

        request.setAttribute(LOG_ID, uuid);

        /**
         * @RequestMapping : HandlerMethod๊ฐ€ ๋„˜์–ด์˜ด
         * ์ •์  ๋ฆฌ์†Œ์Šค : ResourceHttpRequestHandler
         */
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler; // ํ˜ธ์ถœํ•  ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ๋ชจ๋“  ์ •๋ณด
        }

        log.info("REQUEST [{}][{}: {}], [{}]", uuid, method, requestURI, handler);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        String requestURI = request.getRequestURI();
        String method = request.getMethod();
        String uuid = UUID.randomUUID().toString();

        log.info("RESPONSE [{}][{}: {}], [{}]", uuid, method, requestURI, handler);
        if (ex != null) {
            log.error("afterCompletion error!!", ex);
        }
    }
}

 

์œ„์˜ ์†Œ์Šค๋ฅผ ๋น„๊ตํ–ˆ์„ ๋•Œ ๊ฐ€์žฅ ๋จผ์ € ๋ˆˆ์— ๋„๋Š” ๋ถ€๋ถ„์€ ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์ด ์„ธ ๊ฐ€์ง€์˜ ๊ตฌํ˜„๋ถ€๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ฐ๊ฐ์˜ ๊ตฌํ˜„๋ถ€์— ๋Œ€ํ•ด ์„ค๋ช…์„ ํ•ด๋ณด์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. 

 

์ฐธ๊ณ ) HandlerInterceptor์˜ override ๋ฉ”์„œ๋“œ 

preHandle : ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ „์— ํ˜ธ์ถœ, true๋ฉด ๊ณ„์† ์ง„ํ–‰, false๋ฉด ์ข…๋ฃŒ 
postHandle : ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ํ›„์— ํ˜ธ์ถœ (์ •ํ™•ํžˆ๋Š” ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ํ˜ธ์ถœ ํ›„์— ํ˜ธ์ถœ), ๋งŒ์•ฝ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์˜ˆ์™ธ ๋ฐœ์ƒ์‹œ postHandle ์€ ํ˜ธ์ถœ ์•ˆ๋จ 
afterCompletion  : ๋ทฐ๊ฐ€ ๋ Œ๋”๋ง ๋œ ์ดํ›„์— ํ˜ธ์ถœ, ์˜ˆ์™ธ ๋ฐœ์ƒ์‹œ ์˜ˆ๋ฅผ ํ†ตํ•ด ์˜ˆ์™ธ๋ฅผ ํ•™์ธํ•  ์ˆ˜ ์žˆ์Œ -> ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ํ˜ธ์ถœ 

doFilter ๋ฉ”์„œ๋“œ์—์„œ ๋ชจ๋“ ๊ฒƒ์ด ์ด๋ฃจ์–ด์ง€๋Š” ํ•„ํ„ฐ์™€ ๋‹ค๋ฅด๊ฒŒ ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์ „(preHandle), ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•œ ํ›„(postHandle), ๋ทฐ๋ฅผ ๋ Œ๋”๋งํ•œ ํ›„(afterCompletion) ๋“ฑ, Servlet๋‚ด์—์„œ๋„ ๋ฉ”์„œ๋“œ์— ๋”ฐ๋ผ ์‹คํ–‰ ์‹œ์ ์„ ๋‹ค๋ฅด๊ฒŒ ๊ฐ€์ ธ๊ฐ„๋‹ค.

 

๋‹ค์Œ์œผ๋กœ ๊ตฌํ˜„๋ถ€๋ฅผ ์ด๋ฃจ๊ณ  ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋“ค์˜ ์ธ์ž๊ฐ€ ๋‹ค๋ฅธ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ธํ„ฐ์…‰ํ„ฐ๋Š” ๋‹ค์–‘ํ•œ ์‹œ์ ์— ๋”ฐ๋ผ ๋‚˜๋ˆ ์ ธ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ๋•Œ๋ฌธ์— ๊ทธ ์‹œ์ ์— ์กด์žฌํ•˜๋Š” ๋ถ€๊ฐ€์ ์ธ ๋ณ€์ˆ˜๋“ค์„ ์ธ์ž๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ ์ข€ ๋” ๋ณตํ•ฉ์ ์ธ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ณ  Exception ๋ณ€์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์˜ˆ์™ธ ๋กœ์ง์„ ์†์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ Spring์˜ Dispatcher-Servlet ๋‚ด๋ถ€์— ์œ„์น˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Exception์„ @ControllerAdvice์—์„œ @ExceptionHandler๋ฅผ ์ด์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

๋‹ค์Œ์œผ๋กœ ์ธํ„ฐ์…‰ํ„ฐ๋Š” ํ•„ํ„ฐ๋ณด๋‹ค ์ข€ ๋” ์„ธ์„ธํ•œ urlPattern ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค. 

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/api/v1/**")
                .excludePathPatterns("/api/v1/wardrobes");
    }

    @Bean
    public FilterRegistrationBean<Filter> apiAuthorizationFilter() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new ApiAuthorizationFilter());
        filterRegistrationBean.setOrder(2);
        filterRegistrationBean.addUrlPatterns("/api/v1/*");

        return filterRegistrationBean;
    }

excludePathPattern()๊ณผ ๊ฐ™์ด ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์ข€ ๋” ์ž์„ธํ•œ urlPatterns๋ฅผ ์ง€์ •ํ•˜์—ฌ ์„ธ์„ธํ•œ ์ปจํŠธ๋กค์ด ๊ฐ€๋Šฅํ•˜๋‹ค. 

 

๋งˆ์ง€๋ง‰์œผ๋กœ ํ•„ํ„ฐ๋Š” ๋‹ค์Œ ํ•„ํ„ฐ์—๊ฒŒ  request, response ๊ฐ์ฒด๋ฅผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•˜์—ฌ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋‹ค.

public class SomeFilter implements Filter {
  //...
  
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    chain.doFilter(new CustomServletRequest(), new CustomResponse());
  }
}

 

 

์œ„์™€ ๊ฐ™์ด ์ปค์Šคํ„ฐ๋งˆ์ด์ง•๋œ Request, Response ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒฝ์šฐ๋Š” ์–ด๋–ค ๊ฒฝ์šฐ์ผ๊นŒ?

HttpServletRequest์˜ ๋ฐ”๋””๋ฅผ ๋กœ๊น…ํ•˜๋Š” ์ƒํ™ฉ์„ ์˜ˆ๋กœ ๋“ค ์ˆ˜ ์žˆ๋‹ค.  ์š”์ฒญ ์ •๋ณด๋ฅผ ๋‹ด์€ HttpServletRequest ๊ฐ์ฒด์—๋Š” ์š”์ฒญ ๋ฐ”๋””๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” getInputStream() ๋ฉ”์„œ๋“œ๊ฐ€ ์กด์žฌํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฉ”์„œ๋“œ๋Š” ์ตœ์ดˆ 1ํšŒ ํ˜ธ์ถœ ํ›„์—๋Š” ์žฌํ˜ธ์ถœ์‹œ java.io.IOException: Stream closed. ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์ƒํ™ฉ์—์„œ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ ๋กœ๊น… ๋“ฑ์˜ ๋ชฉ์ ์œผ๋กœ ์š”์ฒญ ๋ฐ”๋””๋ฅผ ํš๋“ํ•˜๊ณ ์ž ํ•  ๊ฒฝ์šฐ ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด ๊ฒฝ์šฐ getInputStream()์„ ์žฌํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๋œ HttpServletRequest๋ฅผ ์ œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค.

 

๋‚˜๋งŒ์˜ ์˜ท์žฅ ํ”„๋กœ์ ํŠธ์— Request ํ—ค๋”์™€ Body๋ฅผ ๋กœ๊น…ํ•˜๋Š” ๋ถ€๋ถ„์ด ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ Body๋ฅผ ๋กœ๊น…ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•๋œ Response, Request ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ค„ ํ•„์š”๊ฐ€ ์žˆ์—ˆ๋‹ค.

public class CustomServletWrappingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ContentCachingRequestWrapper wrappingRequest = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper wrappingResponse = new ContentCachingResponseWrapper(response);

        filterChain.doFilter(wrappingRequest, wrappingResponse);
        wrappingResponse.copyBodyToResponse();
    }
}

๊ทธ๋ž˜์„œ ์œ„์™€ ๊ฐ™์ด ์ปค์Šคํ„ฐ๋งˆ์ด์ง•๋œ Request์™€ Response ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ•„ํ„ฐ๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ๊ฐœ๋ฐœํ•˜์—ฌ ๋กœ๊น… ๊ฐœ๋ฐœ์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. 

 

์ฐธ๊ณ ) ContentCachingRequestWrapper, ContentCachingResponseWrapper?

ContentCachingRequestWrapper, ContentCachingResponseWrapper๋Š” inputstream, outputstream์˜ ๋‚ด์šฉ์„ ์บ์‹ฑํ•˜๊ณ , ๋‚˜์ค‘์— ๋‹ค์‹œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” HttpServletResponse์˜ Wrapper์ด๋‹ค.

 

Reference

https://supawer0728.github.io/2018/04/04/spring-filter-interceptor/

 

(Spring)Filter์™€ Interceptor์˜ ์ฐจ์ด

์„œ๋ก Spring์„ ์ตํžŒ์ง€ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•˜์„ ๋•Œ, ํšŒ์› ์ธ์ฆ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ผ์ด ์ƒ๊ฒผ์—ˆ๋‹ค. ๊ทธ ์ธ์ฆ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด Filter์™€ Interceptor๋ฅผ ์กฐ์‚ฌํ–ˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ Filter์™€ Interceptor๋ฅผ ์–ด๋–ค ๊ฒฝ์šฐ์— ์จ์•ผ ์ข‹์€์ง€

supawer0728.github.io

 

https://jsonobject.tistory.com/522

 

Spring Boot, HttpServletRequest ๊ฐ์ฒด์—์„œ ์š”์ฒญ ๋ฐ”๋”” ํš๋“ํ•˜๊ธฐ

๊ฐœ์š” ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€์˜ ์š”์ฒญ ์ •๋ณด๋ฅผ ๋‹ด์€ HttpServletRequest ๊ฐ์ฒด์—๋Š” ์š”์ฒญ ๋ฐ”๋””๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” getInputStream() ๋ฉ”์จ๋“œ๊ฐ€ ์กด์žฌํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฉ”์จ๋“œ๋Š” ์ตœ์ดˆ 1ํšŒ ํ˜ธ์ถœ ํ›„์—๋Š” ์žฌํ˜ธ์ถœ์‹œ java.io.IOException: Stre

jsonobject.tistory.com