Spring MVC 구조
Spring은 MVC 패턴에 프론트 컨트롤러 패턴, 어댑터 패턴이 적용된 구조를 가지고 있다.
Spring MVC 구조 흐름
실행 순서는 다음과 같다.
- Client로 부터 HTTP 요청(request)를 받는다.
- Handler 조회
- Handler Mapping을 통해 요청 URL에 Mapping된 Handler(Controller)를 조회
- Handler를 처리한 Adapter 조회
- Handler를 처리할 수 있는 Handler Adapter 조회
- Handler Adapter 실행
- 알맞은 어댑터가 존재한다면 Handler Adapter에게 요청을 위임한다.
- Handler 실행(호출)
- Handler Adapter가 실제 Handler를 호출하여 실행 및 결과 반환
- Model And View 반환(return)
- Handler Adapter는 Handler가 반환하는 정보를 Model And View 객체로 반환하여 반환
- viewResolver 호출(알맞은 View 요청)
- View Resolver를 찾고 실행
- View 반환
- View Resolver는 View의 논리 이름을 물리 이름으로 전환하는 역할을 수행하고 Rendering 역할을 담당하는 View 객체를 반환
- View Rendering
- View를 통해서 View를 Rendering
요약
DispatcherServlet
- 클라이언트 HTTP Request를 알맞게 파싱하고 클라이언트에게 알맞은 응답을 반환
- 핸들러 목록 정보를 알고 있다.
- 핸들러 어댑터 목록 정보를 알고있다.
HandlerAdapter
- 자신이 처리할 수 있는 Handler인지 확인할 수 있는 기능이 필요하다
- 프론트 컨트롤러에게 요청을 위임받았을 때 핸들러에게 요청을 지시하는 기능이 필요하다
- Handler에게 return 받은 결과를 알맞은 응답으로 변환한다.
Handler
- 요청에 대한 로직을 수행하는 기능이 필요하다.
Dispatcher Servlet
Spring MVC의 프론트 컨트롤러는 Dispatcher Servlet(Servlet의 한 종류)이다.
Dispatcher Servlet의 다이어그램
Dispatcher Servlet은 HttpServlet을 상속 받아서 만든 Servlet의 한 종류이다.
Spring Boot는 Dispatcher Servlet을 서블릿으로 자동으로 등록(내장 Tomcat WAS를 실행하면서 등록한다)하고 모든 URL 경로에 대해서 Mapping 한다. → (urlPatterns="/")
DispatcherServlet의 service( )
Servlet이 호출되면 HttpServlet이 제공하는 service( )가 호출된다.
Spring MVC는 DispatcherServlet의 부모인 FrameworkServlet에서 service( )를 Override 해두었다.
FrameworkServlet.service( ) 를 시작으로 여러 메서드가 호출됨과 동시에 가장 중요한 DispatcherServlet.doDispatch( ) 가 호출된다.
protected void doDispatch() {
...
// 1. 핸들러 조회
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response); // NotFound 404
}
// 2. 핸들러 어댑터 조회 : 핸들러를 처리할 수 있는 어댑터
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 핸들러 어댑터 실행
// 4. 핸들러 어댑터를 통해 핸들러 실행
// 5. ModelAndView 반환
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 여기 안에서 render
processDispatchResult(processedRequest, response, mappedHandler, mv,dispatchException);
...
}
// processDispatchResult()
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
if (mv != null && !mv.wasCleared()) {
// View Render 호출
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
}
// render()
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
// 6. ViewResolver를 통해 View 조회
// 7. View 반환
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// 8. View Rendering
view.render(mv.getModelInternal(), request, response);
}
Spring MVC의 주요 Interface
Spring MVC는 DispatcherServlet 코드의 변경 없이 기능 변경 및 확장이 가능하다.
기능들이 대부분 Interface로 만들어져 있기 때문이다.
이 인터페이스를 구현하면 내가 만든 클래스를 사용할 수 있다.(다형성)
주요 인터페이스는 다음과 같다.
- HandlerMapping
- HandlerAdapter
- ViewResolver
- View
Controller Interface
Controller Interface를 구현하게 되면 개발자가 원하는 Controller(Handler)를 사용할 수 있게 된다.
- 현대에 사용하는 Annotation 기반 Spring의 @Controller와는 역할이 비슷하지만 연관은 없다.
구현 예시
package com.example.springbasicmvc.controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
// Spring Bean 이름을 URL로 설정
@Component("/example-controller")
public class ExampleController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("example-controller가 호출 되었습니다.");
return null;
}
}
@Component
@Component는 Spring Bean에 등록하는 역할을 수행한다
- Spring Bean은 애플리케이션 구성 요소를 정의하는 객체이다.
- 마치 Servlet이 Servlet Container에 등록되는 것과 같다.
ExampleController가 호출되기 위해 필요한 것
HandlerMapping에서 ExampleController를 찾을 수 있어야 한다. Spring Bean의 이름으로 핸들러를 찾을 수 있는 핸들러 매핑이 필요하다.
HandlerMapping 을 통해 찾은 핸들러를 실행할 수 있는 Handler Adapter가 필요하다. Controller Interface를 실행할 수 있는 Handler Adapter를 찾고 실행한다.
💡Spring Boot를 사용하면 이미 개발에 필요한 HandlerMapping과 HandlerAdapter들이 대부분 구현되어 있어서 개발자가 직접 HandlerMapping과 HandlerAdapter를 구현하는 일은 거의 없다.
Spring Boot의 Handler Mapping, Handler Adapter
Spring Boot를 사용하면 개발에 필요한 자동으로 등록되는 HandlerMapping과 HandlerAdapter들이 있다.
HandlerMapping과 HandlerAdapter 모두 우선순위대로 조회한다.
HandlerMapping
우선 순위 순서는 다음과 같다.
- RequestMappingHandlerMapping
- 우선 순위가 가장 높다.
- Annotation 기반 Controller의 @RequestMapping에 사용
- BeanNameUrlHandlerMapping
- 위 예시 코드에서 사용한 것이다.
- Spring Bean Name으로 HandlerMapping한다.
HandlerAdapter
우선 순위 순서는 다음과 같다.
- RequestMappingHandlerAdapter
- Annotation 기반 Controller의 @RequestMapping에서 사용
- HttpRequestHandlerAdapter
- HttpRequestHandler 처리
- SimplerControllerHandlerAdapter
- 위 예시코드에서 사용
- Controller Interface 처리
💡 @RequestMapping은 가장 높은 우선순위의 HandlerMapping인 RequestMappingHandlerMapping과 가장 높은 우선 순위의 HandlerAdapter인 RequestMappingHandlerAdapter 두 가지를 사용하며 현대에 사용하는 Annotaion 기반의 컨트롤러를 지원한다.
View Resolver
View Resolver는 반한된 ModleAndView 객체를 알맞은 View로 전달하기 위해 Dispatcher Servlet에서 ViewResolver를 호출하여 View 정보를 설정하는 역할을 수행한다.
'스프링 프레임워크' 카테고리의 다른 글
ArgumentResolver (1) | 2024.11.27 |
---|---|
HttpMessageConverter (0) | 2024.11.27 |
프론트 컨트롤러 패턴와 어댑터 패턴 (0) | 2024.11.27 |
MVC 패턴 (0) | 2024.11.27 |
Template Engine이란 (0) | 2024.11.27 |