목차
스프링MVC
이전에서의 구현 | 역할 | 스프링에서의 구현 |
FrontController | handlerMappingMap을 생성하고 초기화한다. handlerAdapter를 생성하고 초기화한다. HttpServlet을 상속받아 service메서드를 재정의한다 handlerMappingMap에서 찾은 각 Handler를 Adapter로 넘기고 반환받은 ModelView에서 viewName을 get해서 MyView객체 생성한다 MyView객체의 render메서드를 실행하여 페이지를 이동한다. |
DispatcherServlet |
handlerMappingMap | 주소와 Handler(=각 Controller)들을 맵핑함 (MemberFormControllerV3, MemberSaveControllerV4...) |
HandlerMapping |
MyHandlerAdapter | request대신 사용할 paramMap과 model을 생성한다 Handler(=각 Controller)의 process메서드를 실행하여 view경로를 문자로 받아와서 ModelView객체를 반환함 |
HandlerAdapter |
ModelView | request의 저장공간을 대신함 | ModelAndView |
viewResolver | 고정경로와 viewName을 조합하여 MyView객체를 반환함 | ViewResolver |
MyView | render메서드를 구현하고 viewName으로 RequestDispatcher를 생성하고 forward메서드로 페이지를 이동한다 |
View |
DispatcherServlet Interface구조
org.springframework.web.servlet.DispatcherServlet
스프링 부트는 @Web(urlPatterns="/") DispatcherServlet을 서블릿으로 자동 등록한다.
DispatcherServlet ▶ FrameworkServlet ▶ HttpServletBean ▶ HttpServlet
결국 HttpServlet을 상속받는다. (service메서드를 재정의해야한다)
DispatcherServlet은 코드의 변경 없이 원하는 기능을 변경하거나 확장할 수 있다.
DispatcherServlet의 doService(request, response)메서드를 실행하면 doDispatch(request, response)메서드가 호출된다.
doDispatch 메서드에서는
핸들러를 조회하고
핸들러 어댑터를 조회하고
핸들러 어댑터를 실행해서(handle메서드) 어댑터에서 핸들러 실행하고(process메서드) ModelAndView 반환받음
ModelAndView를 담아 processDispatchResult메서드를 실행한다
processDispatchResult메서드에서는
ModelAndView를 담아 뷰 렌더링을 호출한다(render메서드)
render메서드에서는
ModelAndView에서 viewName을 get하고
뷰 리졸버로(resolveViewName메서드) 고정경로와 핵심경로를 조합하여 View객체 반환받고
View객체의 render 메서드를 수행한다
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
// ★★★★★★ 핸들러 조회
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// ★★★★★★ 핸들러 어댑터 조회
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ★★★★★★ 핸들러 어댑터 실행 -> 어댑터에서 핸들러 실행 -> ModelAndView반환
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// ★★★★★★ 뷰 렌더링 호출
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// ★★★★★★ render 메서드 실행
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// ★★★★★★ 뷰 리졸버로 view 반환받음
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// ★★★★★★ 뷰 렌더링
view.render(mv.getModelInternal(), request, response);
}
}
Handler Mapping과 Handler Adapter
스프링 부트가 자동 등록하는 핸들러 맵핑과 핸들러 어댑터
HandlerMapping | HandlerAdapter |
0순위 : RequestMappingHandlerMapping @RequestMapping에서 사용된다 1순위 : BeanNameUrlHandlerMapping 스프링 빈의 이름으로 핸들러를 찾는다 |
0순위 : RequestMappingHandlerAdapter @RequestMapping에서 사용된다 1순위 : HttpRequestHandlerAdapter HttpRequestHandler 인터페이스에서 사용된다 2순위 : SimpleControllerHandlerAdapter Controller인터페이스에서 사용된다 |
BeanNameUrlHandlerMapping & SimpleControllerHandlerAdapter
1. OldController클래스의 빈 이름을 "/springmvc/old-controller"로 지정한다
2. localhost:8080/springmvc/old-controller로 접속하면
3. 일단 @RequestMapping애노테이션이 있는지 확인한다. => 없다
4. 스프링이 해당되는 핸들러를 빈 이름으로 조회한다 => BeanNameUrlHandlerMapping
5. BeanNameUrlHandlerMapping가 핸들러인 OldController를 DispatcherServlet에게 반환한다
6. DispatcherServlet이 핸들러(OldController)를 처리할 수 있는 어댑터를 찾는다 => SimpleControllerHandlerAdapter
7. SimpleControllerHandlerAdapter에서 핸들러(OldController)를 처리하면서 handleRequest 메서드를 실행한다.
@Component("/springmvc/old-controller")
public class OldController implements Controller {
//@Component애노테이션 : 컴포넌트스캔 시 스프링 빈으로 등록한다,
//빈 이름을 /springmvc/old-controller로 지정한 것이다
//@WebServlet애노테이션을 지정하지 않았는데
//localhost:8080/springmvc/old-controller로 접속이 가능하다.. 어떻게..????
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("OldController.handleRequest");
return null;
}
}
BeanNameUrlHandlerMapping & MyHttpRequestHandler
1. MyHttpRequestHandler클래스의 빈 이름을 "/springmvc/request-handler"로 지정한다
2. localhost:8080/springmvc/request-handler로 접속하면
3. 일단 @RequestMapping애노테이션이 있는지 확인한다. => 없다
4. 스프링이 해당되는 핸들러를 빈 이름으로 조회한다 => BeanNameUrlHandlerMapping
5. BeanNameUrlHandlerMapping가 핸들러인 MyHttpRequestHandler를 DispatcherServlet에게 반환한다
6. DispatcherServlet이 핸들러(MyHttpRequestHandler)를 처리할 수 있는 어댑터를 찾는다 => HttpRequestHandlerAdapter
7. HttpRequestHandlerAdapter에서 핸들러(MyHttpRequestHandler)를 처리하면서 handleRequest 메서드를 실행한다.
@Component("/springmvc/request-handler")
public class MyHttpRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("MyHttpRequestHandler.handleRequest");
}
}
- 사실 @RequestMapping이 가장 많이 사용된다
View Resolver
src/main/resources/application.properties 파일에 아래 코드를 추가해야한다.
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
스프링 부트가 자동 등록하는 뷰 리졸버
뷰 리졸버는 application.properties에 등록한 prefix, suffix 설정정보를 바탕으로 등록된다.
1순위 : BeanNameViewResolver : 빈 이름으로 뷰를 찾아서 반환한다. 2순위 : InternalResourceViewResolver : JSP를 처리할 수 있는 뷰를 반환한다. |
★ JSP의 경우 forward()를 통해서 해당 JSP로 이동해야 렌더링된다.
★ JSP를 제외한 나머지 뷰 템플릿은 forward()과정 없이 바로 렌터링된다
1. SimpleControllerHandlerAdapter에서 핸들러(OldController)를 처리하면서 handleRequest메서드를 실행한다.
2. 반환받은 ModelAndView객체에서 "new-form" 논리경로를 get한다
3. RequestDispatcher에서 viewResolver를 호출하고, BeanNameViewResolver가 "new-form" 으로 지정된 빈을 찾는다 => 없음
4. InternalResourceViewResolver가 실행되고 InternalResourceView를 반환하고
5. InternalResourceView에서 render처리 후 forward메서드를 호출하여 jsp를 실행한다.
@Component("/springmvc/old-controller")
public class OldController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("OldController.handleRequest");
return new ModelAndView("new-form");
}
}
@RequestMapping("주소")
메서드와 함께 사용된다. 메서드명은 임의로 지으면된다.
클라이언트에서 주소를 호출하면 메서드가 호출된다.
@RequestMapping에 해당되는 핸들러는 RequestMappingHandlerMapping,
@RequestMapping에 해당되는 어댑터는 RequestMappingHandlerAdapter 이다.
RequestMappingHandlerMapping의 isHandler메서드는
스프링 빈 중에서 @Controller, @RequestMapping가 클래스 레벨에 붙어있는 경우에만 매핑정보로 인식한다.
스프링MVC적용
v1
- 각 컨트롤러들에 @Controller, @RequestMapping을 적용했다
- @Controller는 클래스레벨에, @RequestMapping은 메서드레벨에 붙이고 공통경로도 모두 작성했다.
- ModelAndView를 사용하여 request.setAttribute를 대신하고 View경로를 저장했다
v2
- 각 컨트롤러들을 한 파일로 통합한다.
- @RequestMapping을 클래스레벨에 붙이고 공통경로를 지정해서 각 메서드에 붙는 @RequestMapping은 핵심경로만 지정하면된다.
v3
- ModelAndView의 저장부분을 Model 파라미터로 대신한다
- 각 메서드의 반환값을 ModelAndView에서 String으로 변환하여 viewName을 직접 반환한다.
- String username = request.getParameter("username")을 @RequestParam("username") String username 으로 변환했다. (Integer.parseInt와 같은 형변환은 자동으로 처리해준다)
- @RequestMapping(method = RequestMethod.GET) 으로 HTTP Method도 구분할 수 있다.
이것을 @GetMapping, @PostMapping으로 더 쉽게 작성할 수 있다.
인프런 - 스프링MVC 백엔드 웹개발 핵심기술
'Backend' 카테고리의 다른 글
Spring Validation 정리 (0) | 2021.12.08 |
---|---|
스프링MVC 기본기능 (0) | 2021.12.03 |
FrontController, View, Model, Adapter, Handler (0) | 2021.12.01 |
서블릿, JSP, MVC패턴 (0) | 2021.11.25 |
서블릿, HTTP Request, HTTP Response, ObjectMapper (0) | 2021.11.24 |