* Chapter 9는 다른 블로그를 참고하여 설정한 상태이기 때문에 스킵합니다.
스프링 MVC 핵심 구성 요소
spring bean 이라 표시한 것은 스프링 빈으로 등록해야하는 것을 의미하며, 회색 배경을 가진 구성요소는 개발자가 직접 구현해야하는 요소이다.
- DispatcherServlet은 모든 연결을 담당한다.
- 웹 브라우저로부터 요청이 들어오면, 그 요청을 처리하기 위해 컨트롤러를 검색하는데, 이때 HandlerMapping이라는 빈 객체에 컨트롤러 검색을 요청한다.
- 웹 요청 경로가 /hello 라면 등록된 컨트롤 빈 중에서 '/hello' 요청 경로를 처리할 컨트롤러를 리턴한다. 컨트롤러 객체를 받은 DispatcherServlet은 HandlerAdapter 빈에게 요청 처리를 위임한다.
- HandlerAdapter는 컨트롤러의 알맞은 메서드를 호출한다.
- 직접 구현한 컨트롤러에게 HandlerAdapter는 결과 값을 리턴 받는다.
- HandlerAdapter는 컨트롤러의 처리 결과를 ModelAndView라는 객체로 변환해서 DispatcherServlet에 리턴한다.
- DispatcherServlet은 결과를 보여줄 뷰를 찾기 위해 ViewResolver 빈 객체를 사용한다. ModelAndView는 컨트롤러가 리턴한 뷰 이름을 담고 있는데, ViewResolver는 이 뷰 이름에 해당하는 View 객체를 찾거나 생성해서 리턴한다.
- DispatcherServlet은 ViewResolver가 리턴한 View 객체에게 응답 결과 생성을 요청한다. JSP를 사용하는 경우 View 객체는 JSP를 실행함으로써 웹 브라우저에 전송할 응답 결과를 생성하고 이로써 모든 과정이 끝이난다.
(CSR: 6번 7번 skip)
컨트롤러와 핸들러
스프링 MVC는 웹 요청을 실제로 처리하는 객체를 핸들러(Handler)라고 표현하고 있으며
@Controller적용 객체나 Controller인터페이스를 구현한 객체는 모두 스프링 MVC 입장에서는 핸들러가 된다. 때문에 특정 요청 경로를 처리해주는 핸들러를 찾아주는 객체를 ControllerMapping이 아닌 HandlerMapping이라고 부른다.
DispatcherServlet은 핸들러 객체의 실제 타입에 상관없이 실행 결과를 ModelAndView라는 타입으로만 받을 수 있으면 된다. 그런데 ModelAndView를 리턴하는 객체도, 그렇지 않은 객체도 존재한다. 따라서 핸들러의 처리 결과를 ModelAndView로 변환해주는 HandlerAdapter가 필요한 것이다.
DispatcherServlet과 스프링 컨테이너
DispatcherServlet은 전달받은 설정 파일을 이용해서 스프링 컨테이너를 생성하는데, 이때 HandlerMapping, 컨트롤러 등 4가지의 빈은 DispatcherServlet이 생성한 스프링 컨테이너에서 구하게 된다. 따라서 DispatcherServlet이 사용하는 설정 파일에 일들 빈에 대한 정의가 포함돼야 한다.
@controller를 위한 HandlerMapping과 HandlerAdapter
HandlerMapping과 HandlerAdapter를 빈으로 등록하지 않았는데 사용이 가능하다. 왜 일까?
@Configuration
@EnableWebMvc
public class MvcConfig {
}
위 설정은 매우 다양한 스프링 빈 설정을 추가해준다. 이 태그가 빈으로 추가해주는 클래스 중 다음의 두 클래스가 포함되어 있다.
- ...생략) servlet.mvc.method.annotation.RequestMappingHandlerMapping
- ...생략) servlet.mvc.method.annotation.RequestMappingHandlerAdapter
RequestMappingHandlerMapping가 위에 설명했던 HandlerMapping이며, RequestMappingHandlerAdapter가 HandlerAdapter이다.
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Model model,
@RequestParam(value = "name", required = false) String name) {
model.addAttribute ("greeting", "안녕하세요, " + name);
return "hello";
}
}
RequestMappingHandlerAdapter 클래스는 "/hello" 요청 경로에 대해 hello()메서드를 호출한다. 이때 Model 객체를 생성해서 첫 번째 파라미터로 전달한다. 비슷하게 이름이 "name"인 HTTP 요청 파라미터의 값을 두번째 파라미터로 전달한다.
RequestMappingHandlerAdapter는 컨트롤러 메서드 결과 값이 String 타입이면 해당 값을 뷰 이름으로 갖는 ModelAndView 객체를 생성해서 DispatcherServlet에 리턴한다. 이때 Model 객체에 보관된 값도 ModelAndView에 함께 전달함으로써 뷰 이름으로 hello를 사용하게 된다.
WebMvcConfigurer 인터페이스와 설정
@EnableWebMvc 애노테이션을 사용하면 @Controller 애노데이션을 붙인 컨트롤러를 위한 설정을 생성한다. 또한 @EnableWebMvc 애노테이션을 시용하면 WebMvconfigurer 타입의 빈을 이용해서 MVC 설정을 추가로 생성한다.
@Configuration
@EnableWebMvc // 스프링 MVC 설정을 활성화 한다.
public class MvcConfig implements WebMvcConfigurer {
// 매핑 경로를 '/'로 주었을 때, jsp/html/css 등을 올바르게 처리하기 위한 설정을 추가한다.
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//JSP를 이용해서 롤러의 실행 결과를 보여주기 위한 설정을 추가한다.
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/view/", ".jsp");
}
}
MvcConfig는 WebMvcConfigurer 인터페이스를 구현하고 있으므로 WebMvcConfigurer타입의 빈이 된다.
@EnableWebMvc 애노테이션을 사용하면 WebMvcConfigurer 타입인 빈 객체의 메서드를 호출해서 MVC 설정을 추가한다. 예를 들어 ViewResolver 설정을 추가하기 위해 configureViewResolvers() 메소드를 오버라이드했다.
JSP를 위한 ViewResolver
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/view/", ".jsp");
}
WebMvcConfigurer 인터페이스에 정의된 ConfigureViewResolvers() 메서드는 ViewResolverResistry 타입의 registry 파라미터를 갖는다. 위와 같이 설정해두면, JSP를 위한 ViewResolver를 설정할 수 있다.
위설정은 InternalResourceViewResolver 클래스를 이용해서 다음 설정과 같은 빈을 등록한다.
@Bean
public ViewResolver viewResolver() {
lnternalResourceViewResolver vr = new lnternalResourceViewResolver() ;
vr.setPreflx("/WEB-1NF/view/");
vr.setSufflx(" .jsp") ;
return vr;
}
DispatcherServlet은 ViewResolver에게 뷰 이름에 해당하는 View 객체를 요청한다. 이때 InternalResourceViewResolver는 "Preflx + 뷰이름+Suffix"에 해당하는 경로를 뷰 코드로 사용하는 View 객체를 리턴한다. DispatcherServlet이 InternalResourceView 객체에 응답 생성을 요청하면 InternalResourceView 객체는 경로에 지정한 JSP 코드를 실행해서 응답 결과를 생성한다.
@RequestMapping("/hello")
public String hello(Model model,
@RequestParam(value = "name", required = false) String name) {
model.addAttribute ("greeting", "안녕하세요, " + name);
return "hello";
}
위의 코드를 예시로 들면 응답 생성을 요청할 때, greeting 키를 갖는 Map 객체를 View 객체에 같이 전달한다. View는 전달받은 Map 객체에 담긴 값을 이용해서 알맞은 응답 결과를 출력한다.
디폴트 핸들러와 HandlerMapping의 우선순위
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
매핑 경로가 '/'인 경우 .jsp로 끝나는 요청을 제외한 모든 요청을 DispatcherServlet이 처리한다. html이나 css 같은 경로를 처리하기 위해서는 WebMvcConfigurer의 configureDefaultServletHandling() 메서드를 사용하는 것이 편리하다.
@Configuration
@EnableWebMvc // 스프링 MVC 설정을 활성화 한다.
public class MvcConfig implements WebMvcConfigurer {
// 매핑 경로를 '/'로 주었을 때, jsp/html/css 등을 올바르게 처리하기 위한 설정을 추가한다.
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//JSP를 이용해서 롤러의 실행 결과를 보여주기 위한 설정을 추가한다.
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/view/", ".jsp");
}
}
DefaultServletHandlerConfigurer#enable() 메서드는 다음의 두 빈 객체를추가한다.
- DefaultServletHttpRequestHandler
- SimpleUrlHandlerMapping
DefaultServletHttpRequestHandler는 클라이언트의 모든 요청을 WAS가 제공하는 디폴트 서블릿에 전달한다. 그리고 SimpleUrlHandlerMapping을 이용해서 모든 경로("/**")를 DefaultServletHttpRequestHandler를 이용해서 처리하도록 설정한다.
웹 브라우저의 요청이 들어오면 DispatcherServlet은 다음과 같은 방식으로 요청을 처리한다.
컨트롤러가 존재한다면
- RequestMappingHandlerMapping을 사용해서 요청을 처리할 핸들러를 검색한다.
- 존재하면 해당 컨트롤러를 이용해서 요청을 처리한다.
컨트롤러가 존재한지 않다면
- 존재하지 않으면 SimpleUrlHandlerMapping 을 사용해서 요청을 처리할 핸들러를 검색한다.
- DefaultServletHandlerConfigurer#enable() 메서드가 등록한 SimpleUrlHandlerMapping 은 "/**'’ 경로(즉 모든 경로)에 대해 DefaultServletHttpRequestHandler를 리턴한다.
- DispatcherServlet은 DefaultServletHttpRequestHandler에 처리를 요청한다.
- DefaultServletHttpRequestHandler는 디폴트 서블릿에 처리를 위임한다.
정리
- DispatcherServlet: 웹 브라우저의 요청을 받기 위한 창구 역할을 하고, 다른 주요 구성 요소들을 이용해서 요청 흐름을 제어하는 역할을 한다.
- HandlerMapping: 클라이언트의 요청을 처리할 핸들러 객체를 찾아준다. 핸들러(커맨드) 객체는 클라이언트의 요청을 실제로 처리한 뒤, 뷰 정보와 모델을 설정한다.
- HandlerAdapter: DispatcherServlet과 핸들러 객체 사이의 변환을 알맞게 처리해준다.
- ViewResolver: 요청 처리 결과를 생성할 View를 찾아주고 View는 최종적으로 클라이언트에 응답을 생성해서 전달한다.
Reference
초보 웹 개발자를 위한 스프링 5 프로그래밍 입문
'Spring > 스프링5 프로그래밍 입문' 카테고리의 다른 글
Chapter 13. 세션, 인터셉터, 쿠키 (0) | 2023.04.10 |
---|---|
Chapter12. MVC 2: 메시지, 커맨드 객체 검증 (0) | 2023.04.07 |
Chapter8. DB연동 (0) | 2023.02.26 |
Chapter7. AOP 프로그래밍 (0) | 2023.02.22 |
Chapter6. 빈 라이프사이클과 범위 (0) | 2023.02.21 |