스프링 프레임워크는 다양한 모듈로 구성된 아키텍처를 기반으로 동작한다. 그중 웹 애플리케이션을 구축할 때 핵심 역할을 담당하는 것이 spring-web 모듈이며, 이 모듈에서 가장 중심이 되는 컴포넌트는 바로 DispatcherServlet이다.
이번 글에서는 Spring Web MVC에서 DispatcherServlet이 어떻게 요청을 처리하는지 MVC 패턴을 중심으로 하나씩 살펴보도록 한다.
DispatcherServlet은 프론트 컨트롤러(Front Controller) 패턴을 구현한 서블릿으로, 모든 HTTP 요청의 진입점이 된다. 웹 애플리케이션에서 클라이언트의 요청을 받아 적절한 컨트롤러로 위임하고, 최종적으로 View를 만들어 사용자에게 응답을 반환하는 역할을 한다.
Spring Boot 프로젝트를 만들면 기본적으로 이 DispatcherServlet이 자동으로 등록되어 있다.
Spring Web은 전통적인 MVC(Model-View-Controller) 패턴을 따른다. 이를 기반으로 DispatcherServlet의 요청 처리 흐름을 살펴보면 다음과 같다.
Client -> DispatcherServlet -> HandlerMapping -> Controller
-> Business Logic / Service -> Model -> ViewResolver -> View
사용자의 브라우저에서 GET /hello 와 같은 요청이 들어오면 이 요청은 DispatcherServlet이 가장 먼저 받는다.
GET /hello HTTP/1.1
DispatcherServlet은 요청을 처리할 적절한 컨트롤러를 찾기 위해 HandlerMapping에게 질의한다.
Spring에서는 @RequestMapping, @GetMapping, @PostMapping 등의 어노테이션을 기준으로 어떤 컨트롤러 메서드가 이 요청을 처리할 수 있는지 결정한다.
@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello() {
return "hello";
}
}
이 경우 /hello 요청은 sayHello() 메서드에 매핑된다.
컨트롤러는 필요하다면 서비스 레이어나 데이터베이스에 접근해서 비즈니스 로직을 처리한다.
@Service
public class HelloService {
public String getMessage() {
return "Hello from Service";
}
}
@RestController
@RequiredArgsConstructor
public class HelloController {
private final HelloService helloService;
@GetMapping("/hello")
public String sayHello() {
return helloService.getMessage();
}
}
컨트롤러에서 데이터를 준비하면, 이를 Model 객체에 담아 View로 전달할 수 있다.
(만약 @RestController가 아닌 @Controller를 사용하는 경우, 템플릿 엔진을 통한 View 렌더링이 가능하다.)
@Controller
public class GreetingController {
@GetMapping("/greet")
public String greet(Model model) {
model.addAttribute("name", "Spring");
return "greeting"; // View 이름
}
}
컨트롤러가 반환한 문자열("greeting")은 ViewResolver에 의해 실제 뷰 파일로 매핑된다.
예를 들어, Thymeleaf를 사용하고 있다면 resources/templates/greeting.html 파일을 찾아 렌더링한다.
<!-- resources/templates/greeting.html -->
<!DOCTYPE html>
<html>
<body>
<h1>Hello, [[${name}]]!</h1>
</body>
</html>
최종적으로 View가 렌더링되어 사용자에게 HTML 응답으로 전송된다.