전체 구조
스프링 MVC의 구조는 다음 그림과 같다.
그림에서 볼 수 있듯이 Front Controller 패턴 구조를 사용하고 있다.
Front Controller 패턴에 대한 내용은 다음 글에 정리하였다.
DispatcherServlet
위 그림의 front controller 역할을 DispatcherServlet 클래스가 맡고 있다.
DispatcherServlet의 상속 관계는 다음과 같다.
DispatcherServlet > FrameworkServlet > HttpServletBean > HttpServlet
이를 보면, DispatcherServlet은 결국 HttpServlet을 상속받으며, 서블릿으로 동작한다는 것을 알 수 있다.
DispatcherServlet에서 doDispatch라는 함수가 주요한 역할을 하는데,
이 함수의 흐름을 파악하면 스프링 MVC의 구조를 대강 파악할 수 있다.
doDispatch 함수는 FrameworkServlet의 service 함수를 시작으로 여러 메서드가 호출되면서 호출된다.
다음은 예외처리, 인터셉터 기능이 제외된 doDispatch 함수이다.
protected void doDispatch(HttpServletRequest request, HttpServletResponseresponse) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
// 1. 핸들러 조회
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2. 핸들러 어댑터 조회
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 핸들러 어댑터 실행 -> 4. 핸들러 실행 -> 5. ModelAndView 반환
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
// 뷰 렌더링 호출
render(mv, request, response);
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
// 6. ViewResolver 호출, 7. View 반환
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// 8. 뷰 렌더링
view.render(mv.getModelInternal(), request, response);
}
동작 순서
동작 순서는 다음과 같다.
순서 | 단계 | 설명 |
---|---|---|
1 | 핸들러 조회 | 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다. |
2 | 핸들러 어댑터 조회 | 실행 가능한 핸들러 어댑터를 조회한다. |
3 | 핸들러 어댑터 실행 | 핸들러 어댑터를 실행한다. |
4 | 핸들러 실행 | 핸들러 어댑터가 실제 핸들러를 실행한다. |
5 | ModelAndView 반환 | 핸들러가 반환하는 정보를 ModelAndView로 변환하여 반환한다. |
6 | ViewResolver 호출 | 뷰 리졸버를 찾고 실행한다. |
7 | View 반환 | 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 변경하고, 뷰 객체를 반환한다. |
8 | 뷰 렌더링 | 뷰를 통해 렌더링한다. |
세부 구조
다음으로 각각의 구조에 대해 세세히 살펴보자.
이때, 핸들러 어댑터와 컨트롤러 사이에 세부적으로 Argument Resolver, ReturnValueHandler, HttpMessageConverter 구조가 존재한다.
HandlerMapping
- 요청 URL에 매핑된 핸들러(컨트롤러)를 반환하는 역할을 한다.
- 인터페이스이며, 구현된 클래스 종류는 다양하다.
순서 핸들러 매핑 유형 설명 0 RequestMappingHandlerMapping @RequestMapping을 사용한 애노테이션 기반 컨트롤러 1 BeanNameUrlHandlerMapping 스프링 빈의 이름으로 핸들러를 찾음 ... ... ... - 실무에서는 RequestMappingHandlerMapping을 많이 사용한다.
HandlerAdapter
- 해당 핸들러가 실행될 수 있도록 하는 어댑터 역할을 한다.
- 핸들러에 맞는 어뎁터가 실행되면 핸들러가 실행되고 ModelAndView를 반환한다.
- 인터페이스이며, 구현된 클래스 종류는 다양하다.
순서 핸들러 어댑터 유형 설명 0 RequestMappingHandlerAdapter @RequestMapping을 사용한 애노테이션 기반 컨트롤러에 대한 처리 1 HttpRequestHandlerAdapter HttpRequestHandler 처리 2 SimpleControllerHandlerAdapter Controller 인터페이스(애노테이션X, 과거에 사용) 처리 ... ... ... - 실무에서는 RequestMappingHandlerAdapter을 많이 사용한다.
ViewResolver / View
- 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
- 인터페이스이며, 구현된 클래스 종류는 다양하다.
순서 뷰 리졸버 유형 설명 1 BeanNameViewResolver 빈 이름으로 뷰를 찾아서 반환 (예: 엑셀 파일 생성 기능에 사용) 2 InternalResourceViewResolver JSP를 처리할 수 있는 뷰를 반환 ... ... ... - JSP 경우 forward()를 통해 JSP로 이동한 후 렌더링 되며, 그 외의 뷰 템플릿은 실제 뷰를 렌더링한다.
- Thymeleaf의 경우 ThymeleafViewResolver를 등록해야 하지만, 라이브러리를 추가하면 자동화해준다.
ArgumentResolver(HandlerMethodArgumentResolver)
- HttpServletRequest , Model, @RequestParam , @ModelAttribute, @RequestBody , HttpEntity 같은 파라미터 기능을 유연하게 처리하는 역할을 한다.
- 해당 파라미터를 지원하는지 체크하며, 지원할 경우 객체를 생성해 컨트롤러 호출 시 넘어가게 만들어 준다.
ReturnValueHandler(HandlerMethodReturnValueHandler)
- ArgumentResolver와 비슷하며, 응답 값을 변환하고 처리한다.
HttpMessageConverter
- 요청의 경우 @RequestBody를 처리하는 ArgumentResolver 또는, HttpEntity를 처리하는 ArgumentResolver가 HTTP 메시지 컨버터를 사용해 필요한 객체를 생성한다.
- 응답의 경우 @ResponseBody를 처리하는 ReturnValueHandler 또는, HttpEntity를 처리하는 ReturnValueHandler가 HTTP 메시지 컨버터를 호출해서 응답 결과를 만든다.
- 인터페이스이며, 구현된 클래스 종류는 다양하다.
순서 Http 메시지 컨버터 유형 클래스 타입 미디어 타입 설명 0 ByteArrayHttpMessageConverter byte[] */* byte[] 데이터 처리 1 StringHttpMessageConverter String */* String 문자로 데이터 처리 2 MappingJackson2HttpMessageConverter 객체 또는 HashMap application/json 관련 application/json 처리 - 요청의 경우 메시지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 canRead() 를 호출하며, 조건을 만족하면 read()를 호출해서 객체를 생성하고 반환한다.
- 응답의 경우 메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 canWrite() 를 호출하며, 조건을 만족하면 write()를 호출해서 HTTP 응답 메시지 바디에 데이터를 생성한다.
확장
위의 세부 구조들은 인터페이스로 제공을 하기 때문에, 필요하다면 기능을 확장할 수 있다.
실제로 기능을 확장할 일이 많지 않지만 필요할 경우 WebMvcConfigurer를 검색해보면 된다.
출처: 김영한, 「스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술」, 인프런
'Web > Spring Boot' 카테고리의 다른 글
PRG 패턴 (0) | 2024.01.08 |
---|---|
스프링 MVC 요청과 응답 (0) | 2024.01.05 |
Front Controller 패턴 (0) | 2024.01.04 |
서블릿, JSP을 사용한 MVC 패턴 (0) | 2024.01.04 |
서블릿 이해하기 (0) | 2024.01.03 |