전체 목록
SpringMedium#70

Spring MVC의 요청 처리 흐름(DispatcherServlet 동작 방식)을 설명해주세요.

#Spring#MVC#DispatcherServlet#
힌트

DispatcherServlet → HandlerMapping → HandlerAdapter → Controller → ViewResolver 순서입니다.

정답 및 해설

Spring MVC의 요청 처리 흐름(DispatcherServlet 동작 방식)을 설명해주세요.

Spring MVC는 Front Controller 패턴을 기반으로 동작합니다. 모든 HTTP 요청이 하나의 진입점인 DispatcherServlet을 통해 처리되며, 이후 각 역할에 특화된 컴포넌트들이 협력하여 응답을 생성합니다.

전체 요청 처리 흐름

클라이언트
    │
    ▼
DispatcherServlet (Front Controller)
    │
    ├─ 1. HandlerMapping → 적절한 Handler(Controller) 탐색
    │
    ├─ 2. HandlerAdapter → Handler 실행
    │
    ├─ 3. Controller → 비즈니스 로직 처리 → ModelAndView 반환
    │
    ├─ 4. ViewResolver → 논리 뷰 이름 → 실제 View 변환
    │
    └─ 5. View → 렌더링 → 응답 반환

단계별 상세 설명

1단계: 클라이언트 요청 수신

클라이언트의 HTTP 요청이 서블릿 컨테이너(Tomcat)를 거쳐 DispatcherServlet에 도달합니다. web.xml 또는 Spring Boot의 자동 설정으로 모든 요청(/)이 DispatcherServlet에 매핑됩니다.

// Spring Boot에서는 자동으로 설정됨
// 직접 설정이 필요한 경우:
@Bean
public ServletRegistrationBean<DispatcherServlet> dispatcherServlet() {
    DispatcherServlet servlet = new DispatcherServlet(applicationContext);
    return new ServletRegistrationBean<>(servlet, "/");
}

2단계: HandlerMapping으로 Handler 탐색

DispatcherServlet이 요청 URL과 HTTP 메서드에 맞는 핸들러(Controller)를 HandlerMapping에 질의합니다.

// 내부적으로 이런 과정이 일어납니다:
// GET /users/1 → UserController.getUser(Long id) 메서드 탐색

@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public UserResponse getUser(@PathVariable Long id) {
        // HandlerMapping이 이 메서드를 찾아냄
        return userService.findById(id);
    }
}

Spring MVC에는 여러 HandlerMapping 구현체가 있으며, RequestMappingHandlerMapping이 가장 일반적으로 사용됩니다.

3단계: HandlerAdapter로 Handler 실행

핸들러의 타입에 따라 적절한 HandlerAdapter가 선택되어 실제 메서드를 호출합니다. HandlerAdapter는 다양한 형태의 핸들러(Controller, HttpRequestHandler 등)를 일관된 방식으로 실행할 수 있게 해줍니다.

// RequestMappingHandlerAdapter가 아래와 같은 작업을 처리합니다:
// 1. @RequestParam, @PathVariable, @RequestBody 등 파라미터 바인딩
// 2. 메서드 실행
// 3. 반환값 처리 (ModelAndView, String, ResponseEntity 등)

4단계: Controller 비즈니스 로직 처리

Controller는 Service를 호출해 비즈니스 로직을 수행하고 결과를 반환합니다.

@Controller  // @RestController가 아닌 경우
@RequestMapping("/orders")
public class OrderController {

    private final OrderService orderService;

    @GetMapping("/{id}")
    public String getOrder(@PathVariable Long id, Model model) {
        Order order = orderService.findById(id);
        model.addAttribute("order", order);
        return "order/detail";  // 논리 뷰 이름 반환
    }
}

5단계: ViewResolver로 View 결정

Controller가 반환한 논리 뷰 이름(예: "order/detail")을 실제 View 객체로 변환합니다.

// application.yml 설정
// spring:
//   mvc:
//     view:
//       prefix: /WEB-INF/views/
//       suffix: .jsp

// "order/detail" → /WEB-INF/views/order/detail.jsp
ViewResolver 종류설명
InternalResourceViewResolverJSP 파일로 변환
ThymeleafViewResolverThymeleaf 템플릿으로 변환
BeanNameViewResolver빈 이름으로 View 탐색

6단계: View 렌더링 및 응답 반환

View가 Model 데이터를 사용해 HTML 등의 응답을 생성하고 클라이언트에 반환합니다.

@RestController의 경우: HttpMessageConverter

@RestController(또는 @ResponseBody)를 사용하면 ViewResolver 대신 HttpMessageConverter가 반환값을 직렬화합니다.

클라이언트
    │
    ▼
DispatcherServlet
    │
    ├─ HandlerMapping → Controller 탐색
    ├─ HandlerAdapter → Controller 실행
    │
    └─ @ResponseBody 감지
           │
           ▼
    HttpMessageConverter (Jackson 등)
           │ Java 객체 → JSON/XML 직렬화
           ▼
    HTTP Response Body
@RestController  // @Controller + @ResponseBody
@RequestMapping("/api/users")
public class UserApiController {

    @GetMapping("/{id}")
    public UserResponse getUser(@PathVariable Long id) {
        // UserResponse 객체가 Jackson에 의해 JSON으로 직렬화됨
        return userService.findById(id);
    }

    @PostMapping
    public ResponseEntity<UserResponse> createUser(@RequestBody CreateUserRequest request) {
        UserResponse response = userService.create(request);
        return ResponseEntity
            .status(HttpStatus.CREATED)
            .body(response);
    }
}

주요 HttpMessageConverter 구현체:

Converter처리 타입
MappingJackson2HttpMessageConverterJSON (application/json)
StringHttpMessageConverter문자열 (text/plain)
ByteArrayHttpMessageConverter바이트 배열 (application/octet-stream)
MarshallingHttpMessageConverterXML (application/xml)

HandlerInterceptor와의 관계

HandlerInterceptor는 DispatcherServlet 이후, Controller 실행 전후에 동작합니다.

@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        // Controller 실행 전 — false 반환 시 요청 중단
        String token = request.getHeader("Authorization");
        if (!tokenService.isValid(token)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) throws Exception {
        // Controller 실행 후, View 렌더링 전
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        // View 렌더링 후 (예외 발생 여부 무관)
        // 리소스 정리에 사용
    }
}

전체 컴포넌트 역할 요약

컴포넌트역할
DispatcherServlet요청을 받아 전체 흐름을 조율하는 Front Controller
HandlerMappingURL과 HTTP 메서드로 적절한 Handler 탐색
HandlerAdapter다양한 형태의 Handler를 일관되게 실행
Controller실제 비즈니스 로직 처리 및 결과 반환
ViewResolver논리 뷰 이름을 실제 View로 변환
ViewModel 데이터를 사용해 최종 응답 생성
HttpMessageConverter@ResponseBody 사용 시 객체를 JSON/XML로 직렬화