JiwonDev

Spring Login#2 ํ•„ํ„ฐ, ์ธํ„ฐ์…‰ํ„ฐ, ArugmentResolver

by JiwonDev

2021.08.29 - [Backend/Spring MVC] - Spring Login#1 ์ฟ ํ‚ค์™€ ์„ธ์…˜

 

Spring Login#1 ์ฟ ํ‚ค์™€ ์„ธ์…˜

๋กœ๊ทธ์ธ (LoginForm.html)์—์„œ ID,PW๋ฅผ ๋ฐ›์•„์„œ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋ฉด ์ฟ ํ‚ค๋ฅผ ์ „์†กํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„ํ•ด๋ณด์ž. ์ฟ ํ‚ค๋ฅผ ์ „์†กํ•˜๋Š” ๊ณผ์ •์€ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋‹ค. ํ•ต์‹ฌ์€ ์ฟ ํ‚ค๋ฅผ ์ด์šฉํ•ด์„œ '์–ด๋–ป๊ฒŒ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€

jiwondev.tistory.com

 

 

# ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์™€ ๊ณตํ†ต๊ด€์‹ฌ์‚ฌํ•ญ

์•ž์—์„œ ๋งŒ๋“  ๋กœ๊ทธ์ธ ์˜ˆ์ œ๋Š” 'ํ™ˆ ํ™”๋ฉด'๋งŒ ๋กœ๊ทธ์ธ ์ƒํƒœ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์คฌ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€์—์„œ ๋กœ๊ทธ์ธ์ด ์œ ์ง€๋˜์–ด์•ผํ•œ๋‹ค. โžก ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋ชจ๋“  ์ปจํŠธ๋กค๋Ÿฌ์— ์ ์šฉ์‹œ์ผœ์•ผํ•œ๋‹ค.

  • ์ด๋ ‡๊ฒŒ ๋ชจ๋“  ๊ณณ์— ์ ์šฉ๋˜๋Š” '๊ณตํ†ต ๊ด€์‹ฌ์‚ฌํ•ญ' ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ์„œ๋ธ”๋ฆฟ์—์„œ๋Š” ํ•„ํ„ฐ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.
    2021.07.18 - [Backend/Java] - #17 Servlet Filter

์ด๋ ‡๊ฒŒ ํ•„ํ„ฐ ๊ฐ์ฒด๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์„œ ๋ชจ๋“  ์„œ๋ธ”๋ฆฟ์— ๊ณตํ†ต ๋กœ์ง์„ ์ ์šฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

// Filter ์ธํ„ฐํŽ˜์ด์Šค๋Š” Servlet๊ณผ ๋น„์Šทํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
package javax.servlet;
 
import java.io.IOException;
 
public interface Filter {
 
    // ์‹ฑ๊ธ€ํ„ด์„ ๊ตฌํ˜„ํ•˜์ง€๋Š” ์•Š์œผ๋‚˜, ๋ณดํ†ต 1๊ฐœ์˜ ๊ฐ์ฒด๋งŒ์„ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.
    // ์›ํ•œ๋‹ค๋ฉด web.xml์˜ ์„ค์ •์— ๋”ฐ๋ผ ์—ฌ๋Ÿฌ๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์งˆ ์ˆ˜๋„ ์žˆ๋‹ค.
    public void init(FilterConfig filterConfig) throws ServletException;
    
    // ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด, ์š”์ฒญ/์‘๋‹ต์„ ์กฐ์ž‘ํ•˜๊ณ 
    // FilterChain์„ ์ด์šฉํ•ด ์กฐ์ž‘ ์ดํ›„ ์›๋ž˜ ๋ชฉ์ ์ง€์ธ ์„œ๋ธ”๋ฆฟ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค.
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException; 
 
    public void destroy();
}

์„œ๋ธ”๋ฆฟ์˜ ํ•„ํ„ฐ๋ฅผ ์Šคํ”„๋ง์˜ AOP๋กœ ์ง์ ‘ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, AOP๋Š” ์›น๊ณผ ๊ด€๋ จ๋œ ๋„๊ตฌ๊ฐ€ ์•„๋‹ˆ๋ผ์„œ ์‚ฌ์šฉํ•˜๊ธฐ ๋ถˆํŽธํ•˜๋‹ค. ๊ทธ๋ž˜์„œ ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ๋”ฐ๋กœ ์ œ๊ณตํ•œ๋‹ค. ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ HttpServletRequest๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด์ค€๋‹ค.

 

์ฐธ๊ณ ๋กœ HTTP ์š”์ฒญ ๋กœ๊ทธ์— ์„ธ์…˜ID๋ฅผ ํ•จ๊ป˜ ๋‚จ๊ธฐ๊ณ  ์‹ถ๋‹ค๋ฉด [ํ•„ํ„ฐ, ์ธํ„ฐ์…‰ํ„ฐ] ๋Œ€์‹ ์— logback mdc๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


# ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋ฅผ ์ด์šฉํ•œ ์„ธ์…˜์œ ์ง€

๊ณผ๊ฑฐ์—๋Š” ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋ฅผ ์ด์šฉํ•ด์„œ ์„ธ์…˜์œ ์ง€๋ผ๋Š” ๊ณตํ†ต ๊ด€์‹ฌ์‚ฌํ•ญ์„ ์ ์šฉ์‹œ์ผฐ์—ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•์€ ํ•ด๋‹น ๋งํฌ์— ์ž์„ธํžˆ ๋‹ค๋ฃจ๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ƒ๋žตํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค. ์•„๋ž˜์˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ ์“ฐ๋ฉด ๋œ๋‹ค.

@WebServlet(name = "loadAppConfig", urlPatterns = { "/loadConfig" }, loadOnStartup = 1)
public class LoggingFilter implements Filter {
 
    // ํ•„ํ„ฐ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์ดˆ๊ธฐํ™”์‹œ ์‚ฌ์šฉ
    // config ๊ฐ์ฒด์—์„œ ํ•„ํ„ฐ ์ •๋ณด, ์ฃผ์–ด์ง„ ํŒŒ๋ผ๋ฉ”ํƒ€, ์„œ๋ธ”๋ฆฟ ์ •๋ณด๋“ฑ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
    public void init(FilterConfig config) throws ServletException {
        System.out.println("ํ•„ํ„ฐ ์ดˆ๊ธฐํ™” ๋จ");
    }
    
    //์š”์ฒญ์‹œ๋งˆ๋‹ค ํ•„ํ„ฐ๊ฐ€ ์‹คํ–‰ํ•  ๋ฉ”์„œ๋“œ
    public void doFilter(ServletRequest request, ServletResponse  response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("์š”์ฒญ์ด ํ•„ํ„ฐ์—์„œ ์ฒ˜๋ฆฌ๋จ");
        chain.doFilter(request, response); // ์กฐ์ž‘์„ ์™„๋ฃŒํ•œ ์š”์ฒญ/์‘๋‹ต์„ ๋ฐ˜ํ™˜.
        System.out.println("์‘๋‹ต์ด ํ•„ํ„ฐ์—์„œ ์ฒ˜๋ฆฌ๋จ");
    }
 
    //ํ•„ํ„ฐ ๊ฐ์ฒด๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ ์‹คํ–‰
    public void destroy() {
        System.out.println("ํ•„ํ„ฐ ์ œ๊ฑฐ๋จ.");
    }
}

 

๋งŒ์•ฝ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ์ดˆ๊ธฐํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์ปดํฌ๋„ŒํŠธ ์ž๋™์Šค์บ”์„ ํ•˜๋Š” ๋ฐฉ๋ฒ•
@ServletComponentScan
@WebServlet(name = "loadAppConfig", urlPatterns = { "/*" }, loadOnStartup = 1)
public Class LogFilter{ ... }
// FilterRegistrationBean ๋ฅผ ์ด์šฉํ•ด์„œ ์ˆ˜๋™์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•
// ๋ฌผ๋ก  ํ•„ํ„ฐ ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ๋“ฑ๋กํ•ด๋„ ์ƒ๊ด€์—†์ง€๋งŒ, ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
import org.springframework.boot.web.servlet.FilterRegistrationBean;

@Configuration
public class WebConfig {
  @Bean
  public FilterRegistrationBean logFilter() {
    FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
    
    filterRegistrationBean.setFilter(new LogFilter());
    filterRegistrationBean.setOrder(1); // ํ•„ํ„ฐ์—์„œ๋Š” ์•ˆ๋˜๋Š” ์ฒด์ธ ์ˆœ์„œ ์ง€์ •๋„ ๊ฐ€๋Šฅ
    filterRegistrationBean.addUrlPatterns("/*"); // ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•  URL ๊ทœ์น™ ์ถ”๊ฐ€
    return filterRegistrationBean;
  }
}

 

์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ์ผ์€ ์—†๊ฒ ์ง€๋งŒ, ์Šคํ”„๋ง์—์„œ ํ•„ํ„ฐ๋ฅผ ์“ด๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿ“‘ LoginCheckFilter

๋”๋ณด๊ธฐ
@Slf4j
public class LoginCheckFilter implements Filter {

  // ๋กœ๊ทธ์ธ์ด ํ•„์š”์—†๋Š” URL ๋ชจ์Œ (whiteList)
  private static final String[] whitelist = {"/", "/members/add", "/login", "/logout", "/css/*"};

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) request;
    String requestURI = httpRequest.getRequestURI();

    HttpServletResponse httpResponse = (HttpServletResponse) response;

    try {
      log.info("์ธ์ฆ ์ฒดํฌ ํ•„ํ„ฐ ์‹œ์ž‘ {}", requestURI);

      if (isLoginWhitelistPath(requestURI)) { // ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•œ ํŽ˜์ด์ง€ ์ ‘์†
        HttpSession session = httpRequest.getSession(false);
        if (session == null || session.getAttribute(SessionConst.LOGIN_MEMBER) == null) {
          // ๋ฏธ์ธ์ฆ ์‚ฌ์šฉ์ž ์š”์ฒญ -> ๋กœ๊ทธ์ธ์œผ๋กœ redirect
          httpResponse.sendRedirect("/login?redirectURL=" + requestURI);
          return;
        }
      }
      
      chain.doFilter(request, response);
    } catch (Exception e) {
      throw e; //์˜ˆ์™ธ์ฒ˜๋ฆฌ & ๋กœ๊น… ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ํ†ฐ์บฃ๊นŒ์ง€ ์˜ˆ์™ธ๋ฅผ ๋ณด๋‚ด์ฃผ์–ด์•ผ ํ•จ
    } finally {
      log.info("์ธ์ฆ ์ฒดํฌ ํ•„ํ„ฐ ์ข…๋ฃŒ {} ", requestURI);
    }
  }

  // ํ™”์ดํŠธ ๋ฆฌ์ŠคํŠธ URL์˜ ๊ฒฝ์šฐ ์ธ์ฆ ์ฒดํฌX
  private boolean isLoginWhitelistPath(String requestURI) {
    // ์Šคํ”„๋ง์˜ PatternMatchUtils๋ฅผ ์‚ฌ์šฉ. ์ด๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๋งค์šฐ ๋ณต์žกํ•ด์ง„๋‹ค.
    return !PatternMatchUtils.simpleMatch(whitelist, requestURI);
  }
}

 

๊ทธ๋ฆฌ๊ณ  ํ•„ํ„ฐ์—์„œ Request์— ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•  URL์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

  • ๋กœ๊ทธ์ธ์„ ์„ฑ๊ณตํ•˜๋ฉด ํ™ˆ ํ™”๋ฉด์œผ๋กœ ๋ณด๋‚ด๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ๊ธฐ์กด์— ๋ณด๋˜ URL๋กœ ๋ณด๋‚ด์ค€๋‹ค.
  @PostMapping("/login")
  public String loginV4(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult,
      @RequestParam(defaultValue = "/") String redirectURL, // redirectURL์„ ํ•„ํ„ฐ์—์„œ ๋ฐ›์•„์˜ด
      HttpServletRequest request) {

    // ... ๊ธฐ์กด ์ฝ”๋“œ์™€ ๋™์ผ ... //

    return "redirect:" + redirectURL;
  }

 


# ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ๋ž€?

์„œ๋ธ”๋ฆฟ์€ ์ž๋ฐ” ์›น ํ‘œ์ค€์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋ผ๋ฉด, ์ธํ„ฐ์…‰ํ„ฐ๋Š” ์Šคํ”„๋ง MVC์—์„œ๋งŒ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

์Šคํ”„๋ง MVC ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ธ”๋ฆฟ์€ Dispatcher Servlet ๋ฐ–์— ์—†๋‹ค. (ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ ํŒจํ„ด)

 

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

public interface HandlerInterceptor {
    
  // ์ปจํŠธ๋กค๋Ÿฌ ์š”์ฒญ ์ „
  default boolean preHandle(HttpServletRequest request, HttpServletResponse response,
      Object handler) throws Exception {
  }

  // ์ปจํŠธ๋กค๋Ÿฌ ํ˜ธ์ถœ ์ดํ›„ (๋ทฐ์— ์ „๋‹ฌํ•  Model ๊ฐ์ฒด ์ œ๊ณต)
  default void postHandle(HttpServletRequest request, HttpServletResponse response,
      Object handler, @Nullable ModelAndView modelAndView)
      throws Exception {
  }

  // ์™„์ „ํ•˜๊ฒŒ HTTP ์š”์ฒญ์ด ๋๋‚œ ์ดํ›„ (Exception ์ œ๊ณต)
  default void afterCompletion(HttpServletRequest request, HttpServletResponse response,
      Object handler, @Nullable Exception ex) throws
      Exception {
  }
}
  • preHandle ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์— ๊ฐ€๊ธฐ์ „์— ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
     โžก preHandle ๋ฐ˜ํ™˜ ๊ฐ’์ด true ์ด๋ฉด ๋‹ค์Œ ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , false์ด๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ฐ”๋กœ ํ˜ธ์ถœํ•œ๋‹ค.
  • postHandle ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ return ์ดํ›„ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
     โžก ๋ทฐ์— ์ „๋‹ฌํ•  Model์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • afterCompletion ์€ HTML ๋ทฐ๊ฐ€ ๋ Œ๋”๋ง๋œ ์ดํ›„ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.

 

  • postHandle ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ˜ธ์ถœ๋˜์ง€ ์•Š์ง€๋งŒ afterCompletion์€ ์˜ˆ์™ธ์™€ ์ƒ๊ด€์—†์ด ํ•ญ์ƒ ํ˜ธ์ถœ๋œ๋‹ค.

๊ทธ๋ž˜์„œ afterCompletion์€ ๋ฌด์Šจ ์˜ˆ์™ธ๊ฐ€ ํ„ฐ์กŒ๋Š”์ง€ ํŒŒ๋ผ๋ฉ”ํƒ€๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. (์˜ˆ์™ธ๊ฐ€ ์—†๋‹ค๋ฉด null)

 


# ์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ ๋“ฑ๋ก๋ฐฉ๋ฒ•

@ 1. ์ธํ„ฐ์…‰ํ„ฐ ๊ฐ์ฒด ๋งŒ๋“ค๊ธฐ

์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ์™€ ๋น„์Šทํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. HandlerInterceptor ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

  • ์ธํ„ฐ์…‰ํ„ฐ ๋˜ํ•œ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋œ๋‹ค. ์ฆ‰ ์‹ฑ๊ธ€ํ†ค์ด๋ฏ€๋กœ ํด๋ž˜์Šค ๋‚ด์— ํ•„๋“œ(์ƒํƒœ๊ฐ’)์„ ์‚ฌ์šฉํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค.
@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) {
      // ๋ฏธ์ธ์ฆ ์‚ฌ์šฉ์ž ์š”์ฒญ -> ๋กœ๊ทธ์ธ์œผ๋กœ redirect
      response.sendRedirect("/login?redirectURL=" + requestURI);
      return false; // ์ปจํŠธ๋กค๋Ÿฌ ์‹คํ–‰
    }

    return true; // ๋‹ค์Œ ์ธํ„ฐ์…‰ํ„ฐ ์‹คํ–‰
  }
}

 

์ฐธ๊ณ ๋กœ preHandle(... Object handler )๋Š” ์Šคํ”„๋งMVC๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ์˜ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๋‹ด๊ณ ์žˆ๋‹ค.

  • HandlerMethod๋กœ ์บ์ŠคํŒ…ํ•˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ ๋นˆ ๊ฐ์ฒด์™€ RequestMapping ๋ฉ”์„œ๋“œ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
  • ResourceHttpRequestHandler๋กœ ์บ์ŠคํŒ…ํ•˜๋ฉด ์š”์ฒญ HTTP ํ—ค๋”์™€ ๊ฐ™์€ ์ •์  ๋ฆฌ์†Œ์Šค ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

Object handler๋ฅผ ์บ์ŠคํŒ…ํ•˜์—ฌ Dispatcher Servlet์ด ์‚ฌ์šฉํ•˜๋Š” ์ •๋ณด๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

 

  • Object handler๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ์„ฑ๋œ๊ฑด์ง€ ์ดํ•ดํ•˜๋ ค๋ฉด ์Šคํ”„๋ง MVC์˜ Dispatcher Servlet์˜ ๋™์ž‘์„ ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.
    โžก Dispatcher Servlet ์—์„œ URI ์š”์ฒญ์— ๋งž๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฐพ๊ณ , ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
    โžก (Object handler) ๋Š” ์„ ํƒ๋œ ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ์ฒด์™€ ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ์ฒด์— ์ „๋‹ฌํ•  HTTP ์š”์ฒญ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. 

// Dispatcher Servlet์˜ ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ    
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    Object handler = getHandler(request); // URL๋ฅผ ์ด์šฉํ•ด ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ์ฒด(handler)๋ฅผ ์ฐพ์•„ ๋“ฑ๋กํ•จ
    
    if (handler == null) {
      response.setStatus(HttpServletResponse.SC_NOT_FOUND);
      return;
    }
    
    MyHandlerAdapter adapter = getHandlerAdapter(handler); // ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ์ฒด์˜ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ฐพ์Œ
      
    // ์–ด๋Œ‘ํ„ฐ๊ฐ€ request, response ๊ฐ์ฒด๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ ํŒŒ๋ผ๋ฉ”ํƒ€ ๋ชจ์–‘์— ๋งž์ถฐ์„œ ์ œ๊ณตํ•จ  
    ModelView mv = adapter.handle(request, response, handler);
    // ๋˜ํ•œ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํ˜•ํƒœ์˜ ๋ฐ˜ํ™˜๊ฐ’์„ ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๋งŒ๋“ฌ
    // [์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ทฐ์˜ ์ด๋ฆ„ "name"์„ ๋ฐ˜ํ™˜], [๋ชจ๋ธ๊ฐ์ฒด(ModelAndView)๋ฅผ ๋ฐ˜ํ™˜]
      
    MyView view = viewResolver(mv.getViewName()); 
    view.render(mv.getModel(), request, response);
  }

 

  • ํ•ธ๋“ค๋Ÿฌ์™€ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ์˜ ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค. (์‹ค์ œ๋กœ๋Š” ๋” ๋ณต์žกํ•˜๋‹ค.)
    โžก ํ•ธ๋“ค๋Ÿฌ์—์„œ ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ์ฒด๋Š” [์Šคํ”„๋ง์˜ HandlerMapping ๊ฐ์ฒด]์— ์˜ํ•ด ๋งค์นญ๋œ๋‹ค.
    โžก ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ์—์„œ ํŒŒ๋ผ๋ฉ”ํƒ€๋Š” [์Šคํ”„๋ง์˜ Arugment Resolver ๊ฐ์ฒด]์— ์˜ํ•ด ๋งค์นญ๋œ๋‹ค.
    โžก ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ์—์„œ ๋ฐ˜ํ™˜๊ฐ’์€ [์Šคํ”„๋ง์˜ ReturnValue Resolver ๊ฐ์ฒด]์— ์˜ํ•ด ๋งค์นญ๋œ๋‹ค.
    โžก ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ Model์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด [์Šคํ”„๋ง์˜ ViewResolver ๊ฐ์ฒด]๊ฐ€ ํ•„์š”ํ•œ View ๊ฐ์ฒด๋ฅผ ์ฐพ์•„์ค€๋‹ค.
  private Object getHandler(HttpServletRequest request) {
    String requestURI = request.getRequestURI();
    return handlerMappingMap.get(requestURI); // ํ•ด๋‹น URL๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ•ธ๋“ค๋Ÿฌ(Controller)๋ฅผ ์ฐพ์Œ
  }

  private MyHandlerAdapter getHandlerAdapter(Object handler) {
    // handler(=์ปจํŠธ๋กค๋Ÿฌ)๊ฐ€ ์ •ํ•ด์กŒ๋‹ค๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” [ํŒŒ๋ผ๋ฉ”ํƒ€, ๋ฐ˜ํ™˜๊ฐ’] ํ˜•ํƒœ๋ฅผ ์ง€์›ํ•˜๋Š” ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ฐพ์Œ.
    // ์–ด๋Œ‘ํ„ฐ๊ฐ€ request, response ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ ํŒŒ๋ผ๋ฉ”ํƒ€๋ฅผ ์ƒ์„ฑํ•จ. 
    for (MyHandlerAdapter adapter : handlerAdapters) {
      if (adapter.supports(handler)) { // (handler instanceof ์–ด๋Œ‘ํ„ฐ)๊ฐ€ true์ธ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ฐพ์Œ;
        return adapter;
      }
  }

 


@ 2. ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก & ์„ค์ •ํ•˜๊ธฐ

  • ์ธํ„ฐ์…‰ํ„ฐ๋„ ํ•„ํ„ฐ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋‹ค์–‘ํ•œ ์„ค์ •์„ ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค. ์‚ฌ์šฉ๋ฒ•๋„ ๋น„์Šทํ•œ ํŽธ์ด๋‹ค. (์กฐ๊ธˆ ๋‹ค๋ฅด๋‹ค)
     โžก @Configuration ๊ฐ์ฒด์—์„œ WebMvcConfigurer ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.
@Configuration
public class WebConfig implements WebMvcConfigurer {
  
  @Override // WebMvcConfigurer.addInterceptors(...)
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LogInterceptor())
        .order(1) // ์ธํ„ฐ์…‰ํ„ฐ ์ˆœ์„œ ์ง€์ •
        .addPathPatterns("/**") // ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์ ์šฉํ•  URL ํŒจํ„ด
        .excludePathPatterns("/css/**", "/*.ico", "/error"); // ์ธํ„ฐ์…‰ํ„ฐ ์ œ์™ธ ํŒจํ„ด

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

}

 

 


# ๊ฟ€ํŒ. ์ธํ„ฐ์…‰ํ„ฐ์ฒ˜๋Ÿผ ArgumentResolver ํ™œ์šฉํ•˜๊ธฐ

  • ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ์œ ์ผํ•˜๊ฒŒ ์ ‘๊ทผ, ์กฐ์ž‘ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์ด ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์— ์ฃผ์–ด์ง€๋Š” ํŒŒ๋ผ๋ฉ”ํƒ€์˜€๋‹ค.
  • ์ธํ„ฐ์…‰ํ„ฐ์— ์ฃผ์–ด์ง€๋Š” request, response ์ž์ฒด๋ฅผ ์กฐ์ž‘ํ•˜๋Š”๊ฑด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ง‰ํ˜€์žˆ๋‹ค.
    ๋ฌผ๋ก  ํ•„ํ„ฐ์—์„œ filter.chain(...)์— ๋„˜๊ธฐ๋Š” request ์ž์ฒด๋ฅผ ๋ฐ”๊ฟ”์น˜๊ธฐํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๊ธดํ•œ๋ฐ...๋ง์ด ๋˜๋Š”์†Œ๋ฆฌ๋ฅผ ํ•˜์ž
     โžก ์Šคํ”„๋ง์—์„œ๋Š” HandlerMethodArgumentResolver ๋ฅผ ์ด์šฉํ•ด, ArgumentResolver๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.
     โžก ์ด๋ฅผ ์ด์šฉํ•ด์„œ ์ธํ„ฐ์…‰ํ„ฐ์—์„œ ํ•˜์ง€๋ชปํ•œ ํŒŒ๋ผ๋ฉ”ํƒ€ ์กฐ์ž‘์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌผ๋ก  ๊ฐ„๋‹จํ•œ ์›น ์„œ๋น„์Šค์—์„œ ์ด๋ ‡๊ฒŒ๊นŒ์ง€ ํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ, ์„œ๋น„์Šค๊ฐ€ ์ปค์ง„๋‹ค๋ฉด ArugmentResolver๋ฅผ ์ด์šฉํ•ด ์ „์šฉ ์–ด๋…ธํ…Œ์ด์…˜์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์„œ ๋งค์šฐ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค. ์•„๋ž˜ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด์ž.

 


@1. ๋‚˜๋งŒ์˜ ์–ด๋…ธํ…Œ์ด์…˜ (@Login) ์ •์˜ํ•˜๊ธฐ

Member ๊ฐ์ฒด์— @Login๋ฅผ ๋‹ฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์„ธ์…˜๊ฐ’์„ ๋ฐ›์•„์˜ค๋„๋ก ArugmentResolver๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME) // ๋Ÿฐํƒ€์ž„์œผ๋กœ ํ•ด์ค˜์•ผ ๋ฆฌ์กธ๋ฒ„๊ฐ€ ์ž‘๋™ํ•œ๋‹ค.
public @interface Login { // ๋‚ด๊ฐ€ ์ง์ ‘ ๋งŒ๋“  @Login ์–ด๋…ธํ…Œ์ด์…˜
}

 

  • HTTP Request์—์„œ ๋ธŒ๋ผ์šฐ์ €์— ์ „์†ก๋œ ์„ธ์…˜(HttpSession, ์ฟ ํ‚ค)๋ฅผ ๋ฐ›์•„์˜จ๋‹ค.
  • ํ•ด๋‹น ์„ธ์…˜์— loginMember ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ํ•ด๋‹น ์„ธ์…˜์— loginMember ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค๋ฉด null์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 
@GetMapping("/") /* ์Šคํ”„๋ง์˜ @SessionAttribute ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• */
  public String homeLoginSpring(
      @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember,
      Model model) {

    //์„ธ์…˜์— ํšŒ์› ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด home
    if (loginMember == null) { return "home";}

    //์„ธ์…˜์ด ์œ ์ง€๋˜๋ฉด ๋กœ๊ทธ์ธ์œผ๋กœ ์ด๋™
    model.addAttribute("member", loginMember);
    return "loginHome";
  }
@GetMapping("/") /* ๋‚ด๊ฐ€ ๋งŒ๋“  ArugmentResolver๋ฅผ ์ด์šฉํ•ด @Login ํƒœ๊ทธ๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• */
  public String homeLoginArgumentResolver(@Login Member loginMember, Model model) {

    //์„ธ์…˜์— ํšŒ์› ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด home
    if (loginMember == null) {
      return "home";
    }

    //์„ธ์…˜์ด ์œ ์ง€๋˜๋ฉด ๋กœ๊ทธ์ธ์œผ๋กœ ์ด๋™
    model.addAttribute("member", loginMember);
    return "loginHome";
  }

 


@ 2. ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ArugmentResolver ๊ตฌํ˜„ํ•˜๊ธฐ

  • HandlerMethodArgumentResolver ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•˜๋ฉด ์†์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
     โžก supportsParameter(...)๋ฅผ ์ด์šฉํ•ด์„œ ํ•ด๋‹น ๋ฆฌ์กธ๋ฒ„๋ฅผ ์ ์šฉํ•  ํŒŒ๋ผ๋ฉ”ํƒ€๋ฅผ ์ •์˜ํ•œ๋‹ค. (๋ฐ˜ํ™˜๊ฐ’์ด true์ธ ๊ฒฝ์šฐ์—๋งŒ ์ ์šฉ)
     โžก resolverArgument(...) ๋ฅผ ์ด์šฉํ•ด์„œ ํ•ด๋‹น ํŒŒ๋ผ๋ฉ”ํƒ€์— ์–ด๋– ํ•œ ๊ฐ’์„ ๋„ฃ์–ด์ค„๊ฑด์ง€ ์ •์˜ํ•œ๋‹ค. 
@Slf4j
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    // ํŒŒ๋ผ๋ฉ”ํƒ€์— @Login ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ ธ๋Š”์ง€ ์—ฌ๋ถ€
    boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);
    
    // ํŒŒ๋ผ๋ฉ”ํƒ€์˜ ํƒ€์ž…์ด Member๋กœ ๋ฐ”์ธ๋”ฉ ๊ฐ€๋Šฅํ•œ์ง€ ์—ฌ๋ถ€ (isAssignableFrom)
    boolean hasMemberType = Member.class.isAssignableFrom(parameter.getParameterType());

    return hasLoginAnnotation && hasMemberType; // true์ธ ๊ฒฝ์šฐ์—๋งŒ resolveArgument() ์‹คํ–‰
  }

  @Override
  public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

    // Resolver์—์„œ requset ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋ฐฉ๋ฒ•
    HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
    
    // HttpSession ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์™€์„œ ํ•ด๋‹น ๋ฉค๋ฒ„์˜ ์„ธ์…˜ID๋ฅผ ์กฐํšŒํ•œ๋‹ค.
    HttpSession session = request.getSession(false);
    
    // ์„ธ์…˜์ด ์—†๋‹ค๋ฉด null, ์žˆ๋‹ค๋ฉด ํ˜„์žฌ Login_member ์„ธ์…˜๊ฐ’ ์ „์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    if (session == null) {
      return null;
    }

    return session.getAttribute(SessionConst.LOGIN_MEMBER);
  }
}


// ์ฐธ๊ณ ๋กœ SessionConst๋Š” ์šฐ๋ฆฌ๊ฐ€ ์˜ˆ์ œ์—์„œ ์ง์ ‘๋งŒ๋“  ์ƒ์ˆ˜์šฉ ๊ฐ์ฒด์ด๋‹ค. ์Šคํ”„๋ง ๊ฐ์ฒด๊ฐ€ ์•„๋‹ˆ๋‹ค
public class SessionConst {
    public static final String LOGIN_MEMBER = "loginMember";
}

 

  • ์ด๋ ‡๊ฒŒ ๋งŒ๋“  ArugmentResolver๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
     โžก @Configuration ๊ฐ์ฒด์— WebMvcConfigurer๋ฅผ ์ƒ์†๋ฐ›์•„ ์ถ”๊ฐ€ํ•œ๋‹ค. ์ธํ„ฐ์…‰ํ„ฐ ์ถ”๊ฐ€์™€ ๋น„์Šทํ•˜๋‹ค.
@Configuration
public class WebConfig implements WebMvcConfigurer {

  @Override // ๋‚ด๊ฐ€ ๋งŒ๋“  @Login ๊ณผ ArgumentResolver ๋“ฑ๋ก
  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("/", "/members/add", "/login", "/logout",
            "/css/**", "/*.ico", "/error");
  }

}

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ