스프링 부트의 기본 의존성 중 하나인 spring-boot-starter-web 모듈을 사용하면 기본적으로 내장 톰캣(Tomcat)을 사용하는 스프링 MVC 구조를 기반으로 동작한다. 아래 그림은 일반적인 웹 요청이 들어왔을 때의 스프링 부트 동작 구조이다.
클라이언트의 요청이 들어오면 *서블릿이 이를 처리해야 하는데, 서블릿은 서블릿 컨테이너에서 관리하며 톰캣이 서블릿 컨테이너의 역할과 WAS(Web Application Server)의 역할을 담당하는 대표적인 컨테이너이다.
서블릿(Servlet)은 클라이언트의 요청을 처리하고, 결과를 반환하는 자바의 웹 프로그래밍 기술이다.
스프링에서는 DispatcherServlet이 서블릿의 역할을 수행한다. 일반적으로 스프링은 톰캣을 embed해 사용한다. 그렇기에 서블릿 컨테이너와 DispatcherServlet은 자동 설정된 web.xml의 설정값을 공유한다.
(1) DispatcherServlet으로 요청(HttpServletRequest)이 들어오면 DispatcherServlet은 핸들러 매핑(Handler Mapping)을 통해 요청 URI에 매핑된 핸들러를 탐색한다. 여기서 핸들러는 컨트롤러(Controller)를 의미한다.
(2) 그리고 핸들러 어댑터(HandlerAdapter)로 컨트롤러를 호출한다.
(3) 핸들러 어댑터에 컨트롤러의 응답이 돌아오면 ModelAndView로 응답을 가공해 반환한다.
뷰 리졸버는 뷰의 렌더링 역할을 담당하는 뷰 객체를 반환한다.
MessageConverter는 요청과 응답에 대해 Body 값을 변환하는 역할을 수행한다.
스프링 부트의 자동 설정은 HttpMessageConverter 인터페이스를 사용한다.
스프링 MVC의 큰 강점은 DispatcherServlet 코드의 변경 없이, 원하는 기능을 변경하거나 확장할 수 있다는 점이다. 주요 인터페이스 목록은 다음과 같다.
핸들러 매핑은 요청 정보를 기준으로 어떤 컨트롤러를 사용할지 선정하는 인터페이스이다. 핸들러 매핑 인터페이스는 여러 구현체를 가지며, 대표적인 구현체는 다음과 같다.
BaseNameUrlHandlerMapping
// "/hello"라는 URL로 들어오는 요청이 helloController() 빈으로 매핑된다.
// MyController 클래스는 @Controller 어노테이션이 붙어있어야 한다.
@Configuration
public class AppConfig {
@Bean("/hello")
public MyController helloController() {
return new MyController();
}
}
ControllerClassNameHanderMapping
// 'HelloWorldController' 클래스가 'helloworld' URL로 매핑된다.
// 즉, '/helloworld' 요청이 들어오면 'HelloWorldController' 클래스가 처리한다.
@Controller
public class HelloWorldController {
// 메서드들 정의
}
SimpleUrlHanderMapping
예시 (XML 설정)
// '/welcome' 요청이 welcomeController 빈으로, '/hello' 요청이 helloController 빈으로 매핑된다.
<beans>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/welcome">welcomeController</prop>
<prop key="/hello">helloController</prop>
</props>
</property>
</bean>
<bean id="welcomeController" class="com.example.WelcomeController"/>
<bean id="helloController" class="com.example.HelloController"/>
</beans>
DefaultAnnotationHanderMapping
@Controller
public class MyController {
@RequestMapping("/hello")
public String hello() {
return "helloView";
}
@RequestMapping("/welcome")
public String welcome() {
return "welcomeView";
}
}