1) MVC 전체 구조
- 이전까지 직접 만들었던 스프링 mvc 구조다.
- 이제 실제 스프링 MVC의 모습이다. 동작 구조가 완전히 같다. 그러나 명칭에만 차이가 있다.
- 프론트 컨트롤러는 디스패쳐 서블릿으로 칭한다. 이게 젤 중요하다.
- 디스패쳐 서블릿도 서블릿이므로, httpServlet을 상속 받아 서블릿으로 동작한다.
- 스프링부트는 디스패쳐 서블릿을 서블릿으로 자동으로 등록하면서, "모든 경로"에 대해서 매핑한다. 어떤 하위 경로에도 디스패쳐 서블릿이 호출되는 것이다.
* 요청 흐름
- 서블릿이 호출되면 HttpServlet이 제공하는 service()가 호출된다.
- 스프링 MVC는 DispatcherServlet 의 부모인 FrameworkServlet 에서 service() 를 오버라이드 해두었다.
- FrameworkServlet.service() 를 시작으로 여러 메서드가 호출되면서 DispacherServlet.doDispatch() 가 호출된다. -> 제일 중요, 핸들러 찾아서 호출해주는 코드다.
* DispacherServlet.doDispatch() 동작과정
- 핸들러를 처리할 수 있는 어댑터를 찾는다
- 어댑터를 실행하면-> 핸들러 어댑터 통해 핸들러 실행되고, ModelAndView가 반환된다.
*2) 핸들러 매핑과 핸들러 어댑터
- 먼저 이전 방식의 스프링 컨트롤러를 구현해보겠다. Controller를 implements한 컨트롤러를 만들어 준다.
- 스프링 빈 이름을 컴포넌트 스캔을 통해 "/springmvc/old-controller" 로 명시해준다.
- 빈 이름으로 url 요청을 보내면 콘솔에 출력이 되어 컨트롤러가 호출됨을 확인한다.
- 콘솔에 출력되려면 두 가지가 필요하다
- 먼저 핸들러 매핑에서 스프링 빈의 이름으로 핸들러를 찾아 가져올 수 있어야 한다.
- 또 찾아온 핸들러를 실행할 수 있는 핸들러 어댑터를 찾고 실행해야 한다.
- 즉 핸들러 매핑, 핸들러 어댑터가 필요하다!
- 핸들러 매핑은 먼저 어노테이션 기반의 컨트롤러인 @RequestMapping을 찾는다. 또 이후 스프링 빈의 이름으로 url과 똑같은 이름의 핸들러를 찾는 @BeanNameUrlHandlerMapping 으로 찾는다.
따라서 최종적으로 OldController 동작 순서는 다음과 같다.
-
핸들러 매핑으로 핸들러 조회
1-1) HandlerMapping 을 순서대로 실행해서, 핸들러를 찾는다.
1-2) 이 경우 빈 이름으로 핸들러를 찾아야 하기 때문에 이름 그대로 빈 이름으로 핸들러를 찾아주는 BeanNameUrlHandlerMapping가 실행에 성공하고 핸들러인 OldController 를 반환한다.
-
핸들러 어댑터 조회
2-1) HandlerAdapter 의 supports() 를 순서대로 호출한다.
2-2) SimpleControllerHandlerAdapter 가 Controller 인터페이스를 지원하므로 대상이 된다.
-
핸들러 어댑터 실행
3-1) 디스패처 서블릿이 조회한 SimpleControllerHandlerAdapter 를 실행하면서 핸들러 정보도 함께 넘겨준다.
3-2) SimpleControllerHandlerAdapter 는 핸들러인 OldController 를 내부에서 실행하고, 그 결과를 반환한다.
정리하자면 OldController 를 실행하면서 사용된 객체는 다음과 같다.
HandlerMapping = BeanNameUrlHandlerMapping
HandlerAdapter = SimpleControllerHandlerAdapter
- 이번에는 핸들러 어댑터로 HttpRequestHandlerAdapter를 써보겠다.
- 핸들러를 세팅해주고 스프링 빈 이름을 지정해준 뒤, 출력 문구를 정해준다.
-
빈 이름으로 url 요청을 보내면 문구가 출력된다.
-
동작 방식은 다음과 같다
1) 먼저 핸들러 매핑으로 빈 네임을 통해 핸들러를 찾는다.
2) 핸들러를 찾으면 어댑터를 뒤져 HttpRequestHandler 어댑터를 찾아 가져온다.
3) 핸들러 어댑터의 handle이 디스패쳐 서블릿에서 호출되고 그 결과를 반환한다.
4) 최종적으로 handleRequest를 호출하고 우리가 지정한 문구가 호출되는 것이다.
-
하지만 가장 우선순위가 높은 핸들러 매핑과 어댑터는 @RequestMappingHandlerMapping @RequestMappingHandlerAdapter 이다.
3) 뷰 리졸버
- OldController에서 ModelAndView를 반환하고 논리적 이름인 "new-form"을 넣어보자
- 서버를 실행하면 오류 페이지가 나오지만 컨트롤러는 호출이 된다. 즉 컨트롤러는 호출되는데 뷰가 없어 오류 페이지가 나오는 것이다.
- 따라서 뷰 리졸버가 필요하다.
- application.properties 에 다음 코드를 추가해 물리 주소로 변환할 url을 넣어준다.
- 이제 정상적으로 뷰가 호출된다. 스프링 부트는 InternalResourceViewResolver 라는 뷰 리졸버를 자동으로 등록하는데, 스프링 부트가 application.properties 에서 설정정보를 가져와 등록해주었기 때문이다.
- 참고로 그냥 물리정보를 직접 적어도 동작은 한다.
- 스프링 부트 뷰 리졸버에는 스프링 빈 이름으로 뷰를 찾아 반환하는 BeanNameViewResolver와 JSP를 처리할 수 있는 뷰를 반환하는 InternalResourceViewResolver가 있다.
- InternalResourceViewResolver가 방금 사용한 리졸버다. 내부에서 자원을 찾을 때 사용한다. jsp처럼 forward를 사용해 실행한다.
- JSP의 경우 forward를 통해 해당 jsp로 이동해야 렌더링이 된다. jsp를 제외한 나머지 뷰 템플릿은 forward 없이 자동 렌더링 된다.