디폴트 핸들러와 HandlerMapping의 우선순위
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
config.MvcConfig
config.ControllerConfig
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
위 web.xml 설정을 보시면 DispatcherServlet에 대한 매핑 경로를 다음과 같이 '/'로 주어져있습니다.
매핑 경로가 '/'인 경우 .jsp로 끝나는 요청을 제외한 모든 요청을 DispatcherServlet이 처리합니다. 즉 /index.html, /css/bootstrap.css와 같이 확장자가 .jsp가 아닌 모든 요청을 DispatcherServlet이 처리하게 됩니다.
그런데 @EnableWebMvc 어노테이션이 등록하는 HandlerMapping은 @Controller을 적용한 빈 객체가 처리할 수 있는 요청 경로만 대응할 수 있습니다. 예를 들어 등록된 컨트롤러가 한 개이고 그 컨트롤러가 @GetMapping("/hello") 설정을 사용한다면, /hello 경로만 처리할 수 있게 됩니다. "/index.html", "/css/bootstrap.css"와 같은 요청을 처리할 수 있는 컨트롤러 객체를 찾지 못해 DispatcherServlet은 404 응답을 전송합니다.
/index.html, /css/bootstrap.css와 같은 경로를 처리하기 위한 컨트롤러 객체를 직접 구현할 수도 있지만, 그보다는 WebMvcConfigurer의 configureDefaultServletHandling() 메서드를 사용하는 것이 편합니다.
package config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/view/", ".jsp");
}
}
위 설정에서 DefaultServletHandlerConfigurer#enable() 메서드는 다음의 두 빈 객체를 추가합니다.
- DefaultServletHttpRequestHandler
- SimpleUrlHandlerMapping
DefaultServletHttpRequestHandler는 클라이언트의 모든 요청을 WAS(웹 애플리케이션 서버, 톰캣이나 웹 로직)가 제공하는 디폴트 서블릿에 전달합니다. 예를 들어 /index.html에 대한 처리를 DefaultServletHttpRequestHandler에 요청하면 이 요청을 다시 디폴트 서블릿에 전달해서 처리하도록 합니다. 그리고 SimpleUrlHandlerMapping을 이용해서 모든 경로("/**")를 DefaultServletHttpRequestHandler를 이용해서 처리하도록 설정합니다.
@EnableWebMvc어노테이션이 등록하는 RequestMappingHandlerMapping의 적용 우선순위가 DefaultServletHttpRequestHandler#enable() 메서드가 등록하는 SimpleUrlHandlerMapping의 우선순위보다 높습니다. 때문에 웹 브라우저의 요청이 들어오면 DispathcerServlet은 다음과 같은 방식으로 요청을 처리합니다.
1. RequestMappingHandlerMapping을 사용해서 요청을 처리할 핸들러 검색합니다.
- 존재하면 해당 컨트로러를 이용해서 처리합니다.
2. 존재하지 않으면 SimpleUrlHandlerMapping을 사용해서 요청을 처리할 핸들러를 검색합니다.
- DefaultServletHttpRequestHandler#enable() 메서드가 등록한 SimpleUrlHandlerMapping은 /** 경로(즉 모든 경로)에 대해 DefaultServletHttpRequestHandler를 리턴합니다.
- DispatcherServlet은 DefaultServletHttpRequestHandler에 처리를 요청합니다.
- DefaultServletHttpRequestHandler 디폴트 서블릿에 처리를 위임합니다.
예를 들어 /index.html 경로로 요청이 들어오면 1번 과정에서 해당하는 컨트롤러를 찾지 못하므로 2번 과정을 통해 디폴트 서블릿이 /index.html 요청을 처리하게 됩니다.
DefaultServletHandlerConfigurer#enable() 외에 몇몇 설정도 SimpleUrlHandlerMapping을 등록하는데 DefaultServletHandlerConfigurer#enable()이 등록하는 SimpleUrlHandlerMapping의 우선순위가 가장 낮습니다. 따라서, DefaultServletHandlerConfigurer#enable()을 설정하면 별도 설정이 없는 묻는 요청 경로를 디폴트 서블릿이 처리하게 됩니다.
'Spring' 카테고리의 다른 글
[Spring] ModelAndView 이용해서 뷰 선택하고 모델을 전달해보기 (0) | 2024.04.29 |
---|---|
[Spring] MVC 주요 에러 발생 원인 알아보기 (Logback 설정) (0) | 2024.04.17 |
[Spring] JSP를 위한 ViewResolver (0) | 2024.04.08 |
[Spring] WebMvcConfigurer 인터페이스와 설정 (0) | 2024.04.03 |
[Spring] DispatcherServlet과 스프링 컨테이너 (0) | 2024.04.03 |