자바 백엔드 입문 25편. Phase 4 Web MVC 시작. 브라우저의 GET 요청이 Spring 컨트롤러까지 도달하는 9단계 흐름과 DispatcherServlet의 역할을 호텔 프런트 데스크 비유로 풀어쓴 학습 노트.
이 글은 자바 백엔드 입문 시리즈 59편 중 25편이에요. Phase 3 SpEL·AOP 3편이 끝났고, 이번 25편부터 Phase 4 Web MVC 5편으로 들어갑니다. 가장 먼저 — "브라우저가 보낸 HTTP 요청이 Spring 컨트롤러까지 어떻게 도달하는가" 의 전체 흐름을 풀어요. 중심에 있는 DispatcherServlet 이 이번 글의 주인공.
DispatcherServlet이 헷갈리는 이유
처음 Spring 코드를 보면 — @RestController 박힌 메서드가 어떻게 알아서 호출되는지가 마법처럼 느껴져요. "Tomcat이 받아서 뭐가 어떻게 거치고 우리 메서드까지 오는가" 의 흐름이 안 잡혀요.
이 글에서는 호텔 프런트 데스크 비유로 풀어요. 호텔 손님(HTTP 요청)이 도착하면 프런트 데스크(DispatcherServlet) 한 직원이 "어느 방인지 확인 → 담당 객실 매니저(컨트롤러) 호출 → 요청 처리 → 응답" 의 전체 흐름을 조정하는 그림. 끝까지 따라오시면 9단계 요청 처리 흐름이 한 번에 들어와요.
DispatcherServlet — 모든 요청의 정문
DispatcherServlet 은 "모든 HTTP 요청의 단일 진입점" 이에요. Spring Web MVC의 핵심 부품이고, Tomcat이 받은 요청을 가장 먼저 받는 자바 객체.
비유 — 호텔에 도착한 손님은 무조건 프런트 데스크부터 거쳐요. "몇 호실이세요? 키 받으세요? 무엇이 필요하세요?" — 프런트가 모든 질문을 받고, 적절한 부서로 안내해줍니다. DispatcherServlet도 똑같아요. 브라우저가 GET /orders/123 보내든 POST /payments 보내든 — 모든 요청이 일단 DispatcherServlet을 통과해요.
┌──────────┐ HTTP ┌────────────────────┐ ┌──────────────┐
│ 브라우저 │ ───────→ │ DispatcherServlet │ → │ 적절한 컨트롤러 │
│ (손님) │ │ (프런트 데스크) │ │ (객실 매니저) │
└──────────┘ 응답 ← └────────────────────┘ ← └──────────────┘
Spring Boot는 시작할 때 DispatcherServlet을 자동으로 생성하고 / 경로 전체에 매핑해줘요. 우리가 직접 박을 일 X.
요청 처리 9단계 — 그림으로 보기
브라우저가 GET /orders/123 보냈을 때 흐름.
[1] 브라우저 ──→ Tomcat (HTTP 서버, 8080 포트)
[2] Tomcat ──→ DispatcherServlet (Spring 진입점)
[3] DispatcherServlet ──→ HandlerMapping ("어느 컨트롤러?")
[4] HandlerMapping ──→ HandlerAdapter ("어떻게 호출?")
[5] HandlerAdapter ──→ 컨트롤러 메서드 (실제 비즈니스 로직)
[6] 컨트롤러 ←── HandlerAdapter (반환값 수신)
[7] HandlerAdapter ──→ MessageConverter (반환값 → JSON·HTML 변환)
[8] DispatcherServlet ──→ Tomcat (응답 본문 + 상태 코드)
[9] Tomcat ──→ 브라우저 (HTTP 응답)
이 9단계가 매 요청마다 일어나요. 우리가 직접 신경 쓰는 건 5번 — 컨트롤러 메서드 — 만. 나머지 8단계는 Spring이 다 자동 처리.
5대 핵심 부품 — Spring MVC의 다섯 직원
DispatcherServlet 한 명이 모든 일을 다 하는 게 아니에요. 보조 부품 5개가 같이 일해요.
| 부품 | 역할 | 비유 |
|---|---|---|
| DispatcherServlet | 요청 진입점, 전체 흐름 조정 | 호텔 프런트 데스크 매니저 |
| HandlerMapping | URL → 컨트롤러 매핑 | 객실 배정표 ("이 손님은 305호") |
| HandlerAdapter | 컨트롤러 메서드 호출 방식 결정 | 객실 매니저 호출 매뉴얼 |
| HandlerInterceptor | 요청 전/후 가로채기 | 로비 보안 직원 |
| HttpMessageConverter | 자바 객체 ↔ JSON/XML 변환 | 통역사 |
| ViewResolver | 뷰 이름 → 실제 HTML 템플릿 | 객실 인테리어 카탈로그 |
이 6명이 매 요청마다 협업해요. 우리가 @RestController + @GetMapping("/orders/{id}") 한 메서드 박으면 — Spring 시작 시 HandlerMapping이 "/orders/{id} 요청은 이 메서드로 보내" 라는 매핑을 자동 등록.
HandlerMapping — URL을 컨트롤러로 연결
HandlerMapping 의 일은 한 줄. "이 URL 요청을 처리할 컨트롤러 메서드를 찾아라".
Spring Boot가 시작될 때 컴포넌트 스캔으로 발견한 모든 @Controller·@RestController 클래스의 @RequestMapping·@GetMapping·@PostMapping 어노테이션을 읽어 — "GET /orders/{id} → OrderController.getOrder()" 같은 매핑 테이블을 한 번에 만들어둬요. 요청이 들어오면 이 테이블에서 즉시 조회해 매칭되는 메서드를 반환.
@RestController
public class OrderController {
@GetMapping("/orders/{id}") // ← HandlerMapping이 이 메서드를 발견·등록
public Order getOrder(@PathVariable Long id) {
return orderService.findById(id);
}
@PostMapping("/orders") // ← 또 다른 매핑
public Order createOrder(@RequestBody OrderRequest req) {
return orderService.create(req);
}
}
같은 OrderController 안에 메서드가 여러 개 있어도 — HandlerMapping이 "메서드 종류 + URL 경로" 조합으로 각각 따로 등록.
HandlerAdapter — 메서드 매개변수 자동 채우기
HandlerMapping이 "이 메서드를 호출해라" 정보를 주면, HandlerAdapter 가 실제 호출을 담당해요. 핵심 역할 — 메서드 매개변수를 HTTP 요청에서 자동으로 추출해 채우기.
@GetMapping("/orders/{id}")
public Order getOrder(
@PathVariable Long id, // URL 경로에서 추출
@RequestParam(required = false) String filter, // 쿼리 스트링에서
@RequestHeader("Authorization") String token, // HTTP 헤더에서
HttpServletRequest request) { // 전체 요청 객체
return orderService.findById(id);
}
이 한 메서드를 호출하려면 4가지 정보가 필요한데 — @PathVariable·@RequestParam·@RequestHeader 어노테이션을 보고 HandlerAdapter가 알아서 HTTP 요청에서 추출해 메서드 매개변수에 끼워줘요. 22편에서 깊이 다룰 부분.
HttpMessageConverter — JSON 자동 변환
컨트롤러가 자바 객체를 반환하면 — Order 객체를 어떻게 JSON으로 변환해 브라우저에 보낼까? HttpMessageConverter 가 이 변환을 자동 처리해요.
@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable Long id) {
return orderService.findById(id); // Order 객체 반환
}
// ↑ 이 반환값을 Jackson(기본 컨버터)이 JSON으로 자동 변환
// → 브라우저로 보내는 응답 본문: {"id": 123, "amount": 10000, ...}
기본 컨버터는 Jackson. Spring Boot가 jackson-databind 의존성 자동 포함이라 별도 설정 없이 JSON 변환이 동작해요. XML 응답이 필요하면 Jackson XML 추가, ProtoBuf 필요하면 그 컨버터 추가 — 패턴은 동일.
HandlerInterceptor — 요청 전/후 가로채기
매 요청이 컨트롤러에 닿기 전/후 공통 작업을 하고 싶으면 — HandlerInterceptor 를 박아요. AOP와 비슷하지만 "HTTP 요청 레벨" 에서만 동작.
@Component
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
log.info("→ {} {}", req.getMethod(), req.getRequestURI());
return true; // false 반환 시 컨트롤러 호출 X
}
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
log.info("← {} {}", res.getStatus(), req.getRequestURI());
}
}
자주 쓰이는 시나리오 — 인증 검증·로깅·요청 카운팅. AOP보다 가볍고 HTTP 컨텍스트(헤더·경로 등) 접근이 자연스러워요.
DispatcherServlet과 ViewResolver — 전통 MVC 패턴
REST API가 아니라 "HTML 페이지를 렌더해서 응답" 하는 옛 패턴에서는 ViewResolver 가 등장해요.
@Controller
public class HomeController {
@GetMapping("/")
public String home(Model model) {
model.addAttribute("message", "Hello");
return "home"; // 뷰 이름 반환 (HTML 파일명 아님)
}
}
컨트롤러가 "home" 이라는 문자열을 반환하면 — ViewResolver가 "home 이라는 이름의 템플릿 파일을 찾아" 처리. 보통 src/main/resources/templates/home.html (Thymeleaf 기본).
현대 백엔드는 거의 다 REST API 패턴(JSON 응답)이라 ViewResolver를 직접 만질 일이 드물어요. 다만 "이런 경로도 있다" 는 알아둘 가치 있어요.
@Controller = ViewResolver 거쳐 HTML 렌더 (전통 MVC). @RestController = @Controller + @ResponseBody, MessageConverter로 JSON 직접 반환. 현대 백엔드는 거의 다 @RestController.
한 줄 정리 — DispatcherServlet은 모든 HTTP 요청의 진입점. HandlerMapping·HandlerAdapter·MessageConverter·HandlerInterceptor·ViewResolver 5명의 보조 부품이 9단계 흐름으로 협업. 우리는 컨트롤러 메서드만 짜면 됨.
시험 직전 한 번 더 — DispatcherServlet 입문자가 매번 헷갈리는 것
- DispatcherServlet = 모든 HTTP 요청의 단일 진입점
- "프런트 컨트롤러 패턴(Front Controller Pattern)" 의 자바 구현
- Tomcat이 요청 받으면 → DispatcherServlet에 전달
- DispatcherServlet은 자바 표준
HttpServlet의 서브클래스 - Spring Boot 시작 시 자동 생성 +
/매핑 — 우리가 박을 일 X - 9단계 흐름 = Tomcat → DispatcherServlet → HandlerMapping → HandlerAdapter → 컨트롤러 → MessageConverter → 응답
- HandlerMapping = URL을 컨트롤러 메서드로 매핑
- HandlerAdapter = 메서드 매개변수 자동 채우기
- HttpMessageConverter = 자바 객체 ↔ JSON/XML 변환
- HandlerInterceptor = 요청 전/후 가로채기 (AOP보다 HTTP 컨텍스트 친화적)
- ViewResolver = 뷰 이름 → HTML 템플릿 파일 매핑
- 기본 MessageConverter = Jackson (JSON)
@Controller= HTML 렌더 (ViewResolver),@RestController= JSON 직접 반환- HandlerInterceptor
preHandle에서false반환 시 컨트롤러 호출 차단 preHandle/postHandle/afterCompletion3단계 콜백@RestController의 반환값은 무조건 응답 본문 (뷰 이름 X)- 같은 URL에 여러 컨트롤러 메서드 매핑 충돌 시 시작 실패
- HandlerMapping은 시작 시 한 번 매핑 테이블 만들어 캐싱
- DispatcherServlet 안에 자동 등록되는 Bean들은 Spring Boot가 자동 설정
- 트래픽 9단계 = 모든 요청 통과. "Spring MVC의 심장"
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 20편 — Bean Scope Singleton과 Prototype
- 21편 — Bean 생명주기 PostConstruct PreDestroy
- 22편 — SpEL 표현식 언어
- 23편 — AOP 횡단 관심사가 뭔가
- 24편 — @Aspect로 첫 AOP 박기
다음 글: