๐Ÿ“Œ ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ

JeongHoHyunยท2025๋…„ 2์›” 13์ผ

Spring MVC

๋ชฉ๋ก ๋ณด๊ธฐ
16/21

์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ

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

์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ ํ๋ฆ„

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

์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

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 {
    }
}
  • preHandle : ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ „์— ํ˜ธ์ถœ๋œ๋‹ค. (ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ํ˜ธ์ถœ ์ „์— ํ˜ธ์ถœ๋œ๋‹ค.)
    • preHandle์˜ ์‘๋‹ต ๊ฐ’์ด true์ด๋ฉด ๋‹ค์Œ์œผ๋กœ ์ง„ํ–‰ํ•˜๊ณ , false์ด๋ฉด ์ง„ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • false์ธ ๊ฒฝ์šฐ ๋‚˜๋จธ์ง€ ์ธํ„ฐ์…‰ํŠธ์™€ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ๋˜ํ•œ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • postHandle : ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ํ›„์— ํ˜ธ์ถœ๋œ๋‹ค. (ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ ํ˜ธ์ถœ ํ›„์— ํ˜ธ์ถœ๋œ๋‹ค.)
  • postHandle : ๋ทฐ๊ฐ€ ๋ Œ๋”๋ง ๋œ ์ดํ›„์— ํ˜ธ์ถœ๋œ๋‹ค.

์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ

  • preHandle : ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ „์— ํ˜ธ์ถœ.
  • postHandle : ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • afterCompletion : ํ•ญ์ƒ ํ˜ธํ’€๋œ๋‹ค. ์ด ๊ฒฝ์šฐ ์˜ˆ์™ธ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์„œ ์–ด๋–ค ์˜ˆ์™ธ์ธ์ง€ ๋กœ๊ทธ ์ถœ๋ ฅ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๐Ÿงน ์ •๋ฆฌ

์ธํ„ฐ์…‰ํ„ฐ๋Š” ์Šคํ”„๋ง MVC ๊ตฌ์กฐ์— ํŠนํ™”๋œ ํ•„ํ„ฐ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. ์Šคํ”„๋ง MVC๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ํŠน๋ณ„ํžˆ ํ•„ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ฉด ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํŽธ๋ฆฌํ•˜๋‹ค.

์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ - ์š”์ฒญ ๋กœ๊ทธ

package hello.login.web.interceptor;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@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 uuid = (String) request.getAttribute(LOG_ID);
        log.info("RESPONSE [{}][{}][{}]", uuid, requestURI, handler);
        if (ex != null) {
            log.error("afterCompletion error!!", ex);
        }
    }
}
  • request.setAttribute(LOG_ID, uuid)
    • ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์˜๊ฒฝ์šฐ ์ง€์—ญ๋ณ€์ˆ˜๋กœ ํ•ด๊ฒฐ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ๋Š” ํ˜ธ์ถœ ์‹œ์ ์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‹ค.
    • preHandle์—์„œ ์ง€์ •ํ•œ ๊ฐ’์„ postHandle, afterCompletion์—์„œ ํ•จ๊ผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด request์— ๋‹ด์•„์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

HandlerMethod

  • ํ•ธ๋“ค๋Ÿฌ ์ •๋ณด๋Š” ์–ด๋–ค ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘์„ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค.
  • ์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•˜๋ฉด ์ผ๋ฐ˜์ ์œผ๋กœ @Controller, @RequestMapping์„ ํ™œ์šฉํ•œ ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ,
  • ์ด ๊ฒฝ์šฐ ํ•ธ๋“ค๋Ÿฌ ์ •๋ณด๋กœ HandlerMethod๊ฐ€ ๋„˜์–ด์˜จ๋‹ค.

ResourceHttpRequestHandler

  • @Controller๊ฐ€ ์•„๋‹ˆ๋ผ /resources/static์™€ ๊ฐ™์€ ์ •์  ๋ฆฌ์†Œ์Šค๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ ResourceHttpRequestHandler๊ฐ€ ํ•ธ๋“ค๋Ÿฌ ์ •๋ณด๋กœ ๋„˜์–ด์˜ค๊ธฐ ๋•Œ๋ฌธ์— ํƒ€์ž…์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

postHandle, afterCompletion

  • ์ข…๋ฃŒ ๋กœ๊ทธ๋ฅผ postHandle ์ด ์•„๋‹ˆ๋ผ afterCompletion ์—์„œ ์‹คํ–‰ํ•œ ์ด์œ ๋Š”, ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ postHandle๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. afterCompletion ์€ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ํ˜ธ์ถœ ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•œ๋‹ค.

์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ - ์ธ์ฆ ์ฒดํฌ

package hello.login.web.interceptor;

import hello.login.web.session.SessionConst;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

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

@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 (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
            log.info("๋ฏธ์ธ์ฆ ์‚ฌ์šฉ์ž ์š”์ฒญ");
            response.sendRedirect("/login?redirectURL=" + requestURI);
            return false;
        }

        return true;
    }
}

โญ๏ธ WebConfig - ์ธํ„ฐ์…‰ํŠธ ๋“ฑ๋ก

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new LoginMemberArgumentResolver());
    }

    @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("/", "/css/**", "/*.ico", "/error", "/login", "/logout", "/member/add");
    }
}
  • registry.addInterceptor(new LogInterceptor) : ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
  • order(1) : ์ธํ„ฐ์…‰ํ„ฐ์˜ ํ˜ธ์ถœ ์ˆœ์„œ๋ฅผ ์ง€์ •ํ•œ๋‹ค. ๋‚ฎ์„ ์ˆ˜๋ก ๋จผ์ € ํ˜ธ์ถœ๋œ๋‹ค.
  • addPathPatterns("/**") : ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ ์šฉํ•  URL ํŒจํ„ด์„ ์ง€์ •ํ•œ๋‹ค.
  • excludePathPatterns() : ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ์ œ์™ธํ•  ํŒจํ„ด์„ ์ง€์ •ํ•œ๋‹ค.

PathPattern ๊ณต์‹ ๋ฌธ์„œ

? ํ•œ ๋ฌธ์ž ์ผ์น˜
* ๊ฒฝ๋กœ(/) ์•ˆ์—์„œ 0๊ฐœ ์ด์ƒ์˜ ๋ฌธ์ž ์ผ์น˜
** ๊ฒฝ๋กœ ๋๊นŒ์ง€ 0๊ฐœ ์ด์ƒ์˜ ๊ฒฝ๋กœ(/) ์ผ์น˜
{spring} ๊ฒฝ๋กœ(/)์™€ ์ผ์น˜ํ•˜๊ณ  spring์ด๋ผ๋Š” ๋ณ€์ˆ˜๋กœ ์บก์ฒ˜
{spring:[a-z]+} matches the regexp [a-z]+ as a path variable named "spring"
{spring:[a-z]+} regexp [a-z]+ ์™€ ์ผ์น˜ํ•˜๊ณ , "spring" ๊ฒฝ๋กœ ๋ณ€์ˆ˜๋กœ ์บก์ฒ˜
{*spring} ๊ฒฝ๋กœ๊ฐ€ ๋๋‚  ๋•Œ ๊นŒ์ง€ 0๊ฐœ ์ด์ƒ์˜ ๊ฒฝ๋กœ(/)์™€ ์ผ์น˜ํ•˜๊ณ  spring์ด๋ผ๋Š” ๋ณ€์ˆ˜๋กœ ์บก์ฒ˜
/pages/t?st.html โ€” matches /pages/test.html, /pages/tXst.html but not /pages/
toast.html
/resources/*.png โ€” matches all .png files in the resources directory
/resources/** โ€” matches all files underneath the /resources/ path, including /
resources/image.png and /resources/css/spring.css
/resources/{*path} โ€” matches all files underneath the /resources/ path and
captures their relative path in a variable named "path"; /resources/image.png
will match with "path" โ†’ "/image.png", and /resources/css/spring.css will match
with "path" โ†’ "/css/spring.css"
/resources/{filename:\\w+}.dat will match /resources/spring.dat and assign the
value "spring" to the filename variable
profile
Java Back-End 2022.11.01 ๐Ÿ’ป~ing

0๊ฐœ์˜ ๋Œ“๊ธ€