Spring MVC 구조와 요청 처리 흐름
한눈에 보기
Spring MVC는 프론트 컨트롤러 패턴 기반으로 모든 요청이 디스패처 서블릿을 거친다. 핸들러 매핑과 어댑터, HttpMessageConverter를 통한 요청 처리 흐름과 필터, 인터셉터 차이를 설명한다.
최종 업데이트 2026.09.17
Spring MVC는 프론트 컨트롤러(Front Controller) 패턴을 기반으로 HTTP 요청을 처리하는 스프링 프레임워크의 웹 계층 모듈이다. 모든 요청은 디스패처 서블릿(DispatcherServlet)이라는 단일 진입점으로 들어오고, 디스패처 서블릿이 핸들러 매핑(Handler Mapping), 핸들러 어댑터(Handler Adapter), 뷰 리졸버(View Resolver) 같은 구성요소에 일을 위임하는 구조로 동작한다. Spring Boot 3.x에서는 이 구성이 자동으로 등록되어 개발자는 컨트롤러 작성에만 집중할 수 있다.
프론트 컨트롤러 패턴과 DispatcherServlet
프론트 컨트롤러 패턴은 요청마다 서블릿을 따로 만들지 않고, 하나의 컨트롤러가 모든 요청을 받아 공통 처리를 한 뒤 세부 처리를 위임하는 패턴이다. Spring MVC에서 이 역할을 맡는 것이 디스패처 서블릿이다. 인코딩, 예외 처리, 뷰 렌더링 위임 같은 공통 관심사를 한곳에서 처리하므로 개별 컨트롤러는 비즈니스 요청 처리에만 집중한다. 스프링 부트는 디스패처 서블릿을 자동 구성으로 등록하고 기본적으로 모든 경로에 매핑한다. 디스패처 서블릿은 특정 컨트롤러 방식에 의존하지 않고 HandlerMapping, HandlerAdapter 같은 인터페이스에만 의존하기 때문에, 애너테이션(Annotation) 기반 컨트롤러든 함수형 엔드포인트든 같은 흐름으로 처리할 수 있다.
요청 처리 흐름
클라이언트 요청이 응답으로 돌아가기까지의 흐름은 다음과 같다.
- 클라이언트의 요청이 서블릿 컨테이너의 필터(Filter)를 거쳐 디스패처 서블릿에 도착한다.
- 디스패처 서블릿이 HandlerMapping에 물어 요청 URL과 메서드에 맞는 핸들러(컨트롤러 메서드)를 찾는다. @RequestMapping 기반이라면 RequestMappingHandlerMapping이 담당한다.
- 찾은 핸들러를 실행할 수 있는 HandlerAdapter를 조회한다. 애너테이션 컨트롤러는 RequestMappingHandlerAdapter가 처리한다.
- 핸들러 실행 전에 등록된 인터셉터(Interceptor)의 preHandle이 호출된다.
- 핸들러 어댑터가 ArgumentResolver로 @PathVariable, @RequestBody 같은 파라미터를 만들어 컨트롤러 메서드를 호출한다.
- 반환 값을 ReturnValueHandler가 해석한다. 뷰 이름이면 ModelAndView로, @ResponseBody면 HttpMessageConverter로 바로 응답 본문을 쓴다.
- 뷰 렌더링이 필요한 경우 ViewResolver가 뷰 이름을 실제 View 객체로 바꾸고, 모델 데이터를 넣어 렌더링한다.
- 인터셉터의 postHandle과 afterCompletion이 호출되고 응답이 클라이언트로 돌아간다. 처리 중 예외가 나면 HandlerExceptionResolver가 개입하며, @RestControllerAdvice의 @ExceptionHandler가 이 지점에서 동작한다.
REST 응답과 HttpMessageConverter
REST API에서는 뷰 렌더링 대신 객체를 JSON으로 직렬화해 응답 본문에 쓴다. 이를 담당하는 것이 HttpMessageConverter이며, 요청의 Content-Type과 응답의 Accept 헤더를 보고 적절한 컨버터를 고른다. JSON에는 잭슨(Jackson) 기반 컨버터가 기본으로 쓰인다.
@RestController
@RequestMapping("/api/members")
public class MemberController {
private final MemberService memberService;
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@GetMapping("/{id}")
public MemberResponse find(@PathVariable Long id) {
return memberService.findById(id);
}
@PostMapping
public ResponseEntity<MemberResponse> create(@RequestBody @Valid MemberCreateRequest request) {
MemberResponse response = memberService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
}
@RestController는 @Controller에 @ResponseBody를 합친 애너테이션으로, 모든 메서드의 반환 값이 뷰 이름이 아니라 응답 본문으로 처리된다. @RequestBody 파라미터는 요청 본문을 컨버터가 객체로 역직렬화한 결과다.
필터와 인터셉터 비교
공통 처리를 어디에 둘지 결정할 때 필터와 인터셉터의 차이를 알아야 한다.
| 구분 | 필터 | 인터셉터 |
|---|---|---|
| 소속 | 서블릿 컨테이너 표준 | Spring MVC |
| 실행 위치 | 디스패처 서블릿 도달 이전 | 핸들러 실행 전후 |
| 접근 정보 | ServletRequest, ServletResponse | 선택된 핸들러 정보까지 접근 가능 |
| 예외 처리 | @ExceptionHandler 적용 밖 | 스프링 예외 처리 흐름 안 |
| 대표 용도 | 인코딩, CORS, 보안(Spring Security 필터 체인) | 인증 확인, 로깅, 권한 검사 |
디스패처 서블릿보다 앞 단계에서 요청 전체를 다뤄야 하면 필터, 스프링 컨텍스트의 빈(Bean)과 핸들러 정보를 활용해야 하면 인터셉터가 적합하다. 컨트롤러 메서드 호출을 세밀하게 가로채야 한다면 AOP(Aspect Oriented Programming)도 선택지가 된다.
면접에서 이렇게 물어본다
- 디스패처 서블릿이 요청을 받아 응답을 반환하기까지의 흐름을 구성요소 이름과 함께 설명해 보라.
- 프론트 컨트롤러 패턴을 사용해서 얻는 이점이 무엇인지 말해 보라.
- HandlerMapping과 HandlerAdapter를 인터페이스로 분리한 이유를 설명해 보라.
- @RestController의 반환 값이 JSON으로 변환되는 과정을 HttpMessageConverter와 연결해 설명해 보라.
- 필터와 인터셉터의 차이, 그리고 각각 어떤 상황에 쓰는지 예를 들어 말해 보라.
Spring / JPA에서 같이 읽기
- JPA(Java Persistence API)JPA는 자바 객체와 관계형 데이터베이스 테이블의 매핑을 표준으로 정의한 ORM 명세다. 명세와 구현체의 관계, 영속성 컨텍스트의 기능, 엔티티 생명주기, N+1 문제까지 면접 관점에서 정리한다.
- 더티 체킹(Dirty Checking)더티 체킹은 영속성 컨텍스트가 엔티티 변경을 감지해 플러시 시점에 UPDATE 쿼리를 자동 생성하는 JPA 기능이다. 스냅샷 비교 원리, 플러시 시점, 준영속 엔티티와 벌크 연산의 주의점을 다룬다.
- SpringApplication과 부트스트랩 과정SpringApplication은 스프링 부트의 main 메서드에서 부트스트랩을 총괄하는 클래스다. run() 내부의 환경 준비, 컨텍스트 생성, refresh, 러너 실행 순서와 자동 구성 원리를 설명한다.
- 스프링 빈 스코프(Bean Scope)빈 스코프는 스프링 컨테이너가 빈을 언제 생성하고 어디까지 공유할지 정하는 규칙이다. 싱글턴과 프로토타입의 동작 차이, 웹 스코프, 스코프 혼용 시 문제와 ObjectProvider, 프록시 해결법을 정리한다.
이 개념, 내 코드에서는 어떻게 물어볼까요
데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.
공고 넣고 시작하기