🧑🏫 목표
- MVC 패턴과 Spring MVC의 구조 및 주요 인터페이스를 이해하고, 이를 기반으로 웹 애플리케이션의 흐름과 동작 방식을 학습한다.
- Spring Boot를 활용하여 View Resolver, Handler Mapping 등 주요 컴포넌트의 작동 순서를 파악하고, 실습을 통해 이론을 실제로 적용해 본다.
MVC 패턴에 프론트 컨트롤러 패턴, 어댑터 패턴 적용

DispatcherServlet: Spring의 프론트 컨트롤러View: 인터페이스로 구성되어있다. 확장성을 가지고 있다.→ Spring MVC의 핵심 기능

(urlPatterns="/"): 모든 요청을 받는다.HttpServlet을 상속 받아서 제공하는 service() 오버라이딩
👉 HttpServlet > FrameworkServlet > DispatcherServlet
HttpServlet
FrameworkServlet 

▶︎ 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는 DispatcherServlet 코드의 변경 없이 기능변경 및 확장이 가능하다. 기능들이 대부분 Interface로 만들어져 있기 때문이다.
→ 인터페이스를 implements 하여 구현하면 내가 만든 클래스를 사용할 수 있다. (다형성)
🧑💻 개발자의 입장에서는...
Spring Bean 이름을 URL로 설정
호출되면 실행되어야 하는 비즈니스 로직


🚩 실행 흐름
위에서 필요한 HandlerMapping과 HandlerAdapter는 Spring Boot가 구현
HandlerMapping
@RequestMapping에 사용HandlerAdapter
@RequestMapping에서 사용HttpRequestHandlerAdapter
기존 방식에서 사용하는 Servlet과 가장 유사한 Handler
▶︎ 예시코드
// 인터페이스
public interface HttpRequestHandler {
void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}
@Component("/request-handler")
public class ExampleRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("request-handler Controller 호출");
}
}

1. HandlerMapping 으로 핸들러 조회
supports() 를 우선순위 순서대로 호출 → instanceof 연산자 사용
ha.handle()// Spring Bean 이름을 URL로 설정
@Component("/view-controller")
public class ViewController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("view-controller가 호출 되었습니다.");
// "test"는 논리적인 ViewName이다. ViewResolver가 물리적인 이름으로 변환해야 한다.
return new ModelAndView("test");
}
}
논리이름 "test" → 물리이름 "/WEB-INF/views/test.jsp" 로 변환 필요
ViewResolver
application.properties 설정
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
설정을 기반으로 SpringBoot가 InternalResourceViewResolver를 만든다.
ViewName으로 View를 찾지 못하는 경우(= View가 존재하지 않는 경우)

☹️ Whitelabel Error Page 표시
위에서 필요한 ViewResolver는 Spring Boot에서 구현
application.properties 설정 파일에 등록한 prefix, suffix 설정 정보를 사용하여 ViewResolver 등록Mapping된 Handler의 HandlerAdapter 호출하고 HandlerAdapter는 논리 View Name을 얻음
ModelAndView 형태로 반환받고 DispatcherServlet이 ViewResolver를 위의 우선순위 대로 호출
호출된 InternalResourceViewResolver

buildView(String viewName)
(물리이름이 일치하는) 알맞은 View를 반환
InternalResourceView
renderMergedOutputModel() → Model을 Request로 바꾼다.
💡 Thymeleaf는 View와 Resolver가 이미 존재한다. 라이브러리 의존성만 추가해주면 SpringBoot가 모두 자동으로 해준다. 즉,
return “viewName”;만으로 View가 Rendering 된다.