프론트 컨트롤러 패턴을 직접 구현해보면서 공통 로직을 하나의 컨트롤러로 모을 수 있다는 것을 확인했다.
Spring MVC 역시 동일한 구조를 사용하며, 그 중심에는 DispatcherServlet이 존재한다.
이번 글에서는 Spring MVC가 요청을 처리하는 전체 구조와 핵심 컴포넌트들을 정리한다.
Spring MVC의 핵심은 DispatcherServlet이다.
모든 HTTP 요청은 DispatcherServlet을 통해 처리된다.
DispatcherServlet은 Front Controller 패턴을 기반으로 만들어진 컴포넌트이며 요청 처리 흐름 전체를 관리한다.
즉,
• 요청을 받고
• 컨트롤러를 찾고
• 컨트롤러를 실행하고
• View를 찾아 응답을 반환하는
전체 과정을 조정하는 역할을 한다.
Spring MVC에서 요청이 처리되는 전체 흐름은 다음과 같다.
Client
│
▼
DispatcherServlet
│
▼
HandlerMapping
│
▼
HandlerAdapter
│
▼
Controller
│
▼
ModelAndView
│
▼
ViewResolver
│
▼
View
│
▼
Response
각 단계의 역할을 살펴보자.
정의
요청 URL에 맞는 컨트롤러를 찾아주는 역할
클라이언트 요청이 들어오면 DispatcherServlet은 먼저 HandlerMapping을 통해 실행할 컨트롤러를 찾는다.
예를 들어 다음과 같은 컨트롤러가 있다고 가정하자.
@Controller
@RequestMapping("/members")
public class MemberController {
@GetMapping
public String members() {
return "members";
}
}
요청
GET /members
HandlerMapping은 @RequestMapping 정보를 기반으로
MemberController.members()
HandlerMapping이 컨트롤러를 찾았다고 해서 바로 실행할 수 있는 것은 아니다.
왜냐하면 컨트롤러의 형태가 다양할 수 있기 때문이다.
그래서 등장한 것이 HandlerAdapter이다.
정의
찾아낸 컨트롤러를 실제로 실행해주는 역할
HandlerAdapter는 다음 두 가지를 수행한다.
1. 해당 컨트롤러를 실행할 수 있는지 확인
2. 컨트롤러 실행
RequestMappingHandlerAdapter
HandlerAdapter를 통해 컨트롤러가 실행된다.
@GetMapping("/members")
public String members(Model model) {
List<Member> members = memberRepository.findAll();
model.addAttribute("members", members);
return "members";
}
컨트롤러는 다음 두 가지 정보를 반환한다.
1. View 이름
컨트롤러의 반환값은 내부적으로 ModelAndView 형태로 변환된다.
ModelAndView는 다음 두 가지 정보를 가진 객체이다.
ModelAndView
├ ViewName
└ Model (데이터)
즉
어떤 화면(View)을 보여줄지
어떤 데이터(Model)를 전달할지를 함께 담고 있는 객체이다.
컨트롤러는 실제 View 경로를 반환하지 않는다.
예
return "members";
이것은 실제 경로가 아닌 논리적인 View 이름(Logical View Name)이다.
ViewResolver의 역할은
논리적인 View 이름을 실제 View 경로로 변환하는 것이다.
예
members
↓
/WEB-INF/views/members.jsp
대표 구현체
InternalResourceViewResolver
View는 실제 화면을 생성하는 역할을 한다.
대표적인 View 기술
JSP
Thymeleaf
예를 들어 JSP에서는 다음과 같이 Model 데이터를 사용할 수 있다.
<c:forEach var="member" items="${members}">
<tr>
<td>${member.username}</td>
<td>${member.age}</td>
</tr>
</c:forEach>
View는 Model 데이터를 기반으로 HTML을 생성한다.
Spring MVC에서 View에 데이터를 전달하는 방식은 여러 가지가 있다.
Model
가장 많이 사용하는 방식이다.
@GetMapping("/members")
public String members(Model model) {
List<Member> members = memberRepository.findAll();
model.addAttribute("members", members);
return "members";
}
특징
데이터는 Model에 저장
View 이름은 String으로 반환
ModelAndView
Model과 View를 하나의 객체로 묶는 방식이다.
@GetMapping("/members")
public ModelAndView members() {
List<Member> members = memberRepository.findAll();
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", members);
return mv;
}
ModelMap
Model과 비슷하지만 Map 구조로 동작한다.
@GetMapping("/members")
public String members(ModelMap model) {
model.addAttribute("members", members);
return "members";
}
DispatcherServlet
→ HandlerMapping (컨트롤러 찾기)
→ HandlerAdapter (컨트롤러 실행)
→ Controller
→ ModelAndView
→ ViewResolver
→ View
→ Response
각 구성 요소의 역할
DispatcherServlet → 전체 흐름 제어
HandlerMapping → 실행할 컨트롤러 찾기
HandlerAdapter → 컨트롤러 실행
ViewResolver → View 경로 변환
Model → View에 전달할 데이터