Spring MVC의 DispatcherServlet

저니·2023년 5월 12일
0

Spring

목록 보기
1/2

개요

스프링 MVC로 사용자의 HTTP 요청을 처리하게 하려면 아래처럼 간단한 컨트롤러를 작성해서 만들 수 있다.

@RestController
@RequestMapping("/hello")
public class HelloController {

    @GetMapping
    public String hello() {
        return "hello";
    }
}

이게 가능한 이유는 복잡한 구현을 Spring이 프레임워크 차원에서 숨겨두고 있기 때문인데, 내부에서 어떤 과정을 거쳐서 처리되는지 알아보자.

DispatcherServlet

스프링 MVC는 HTTP 프로토콜을 통해 들어오는 모든 요청을 중앙집중식으로 처리하는 프론트 컨트롤러(Front Controller)인 DispatcherServlet 을 통해, HTTP 요청을 처리한다.

요청이 들어오면 서블릿 컨테이너(ex. Tomcat)가 요청을 받게 되는데, 공통 작업은 DispatcherServlet에서 처리하고, 이외 작업은 적절한 세부 컨트롤러(우리가 위에서 코드로 작성한)로 위임한다.

DispatcherServlet를 통한 요청 처리 흐름

출처 : 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

위 이미지를 보면 HTTP 요청을 받은 DispatcherServlet이 어떤 과정을 거쳐서 핸들러를 호출하고 결과를 반환하는지 전체적으로 볼 수가 있다.

DispatcherServlet은 위 그림처럼, HTTP의 요청에 따라(URL이나 파리미터 정보 등) 어떤 컨트롤러에 작업을 위임할지를 결정하게 되는데, 이 때 컨트롤러는 핸들러 매핑 전략을 이용한다(실제로 전략 패턴이 적용되었기 때문에 전략이라고 부른다).

URL에 따라 어떤 컨트롤러가 이를 처리할지를 매핑해주는 전략(앞서본 HelloController 클래스 처럼)을 만들어 DI로 제공해주기만 하면 된다.

어떤 컨트롤러가 요청을 처리하게 할지를 정했다면, 해당 컨트롤러의 메서드를 호출해서 실제 웹 요청을 처리하는 작업을 위임해주면 된다. 하지만 DispatcherServlet 이 위임하는 컨트롤러 오브젝트에는 어떠한 제약 조건도 없기 때문에(특정 인터페이스를 따라야 한다거나 이런게 없다) DispatcherServlet은 오브젝트에 어떤 메서드가 있는지 모르므로 직접 호출할 수가 없게 된다.

여기서 오브젝트에 따라 어떻게 메서드를 호출할 지는 핸들러 어댑터가 결정한다. DispatcherServlet 은 컨트롤러를 조회하고, 어떤 어댑터가 해당 컨트롤러를 처리할 수 있는지 찾은 후, 해당 어댑터에 호출을 위임한다.

그리고 어댑터는 컨트롤러가 반환한 값을 처리할 ViewResolver 를 호출해서 최종 결과를 반환하게 된다.

DispatcherServlet의 전략

HandlerMapping

요청을 처리할 Handler를 선정하는 역할이다

public interface HandlerMapping {
	// ...생략

	@Nullable
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

URL과 요청 정보를 기준으로, 어떤 컨트롤러를 사용할 것인지를 결정한다.

기본적으로는 BeanNameUrlHandlerMappingRequestMappingHandlerMapping 두 가지가 등록되어 있다.

BeanNameUrlHandlerMapping은 빈 이름이 url과 일치하는 컨트롤러를 찾고,
RequestMappingHandlerMapping@RequestMapping 에 명시된 url과 일치하는 컨트롤러를 찾는다.

그리고 getHandler 메서드를 보면 핸들러 매핑은 컨트롤러를 찾고 HandlerExecutionChain 을 돌려주는데, 이 체인은 하나이상의 HandlerInterceptor 를 거쳐서 컨트롤러가 실행될 수 있도록 한다. (인터셉터를 전혀 등록해 주지 않았다면 바로 컨트롤러가 실행된다)

HandlerInterceptor

HandlerInterceptor 인터페이스를 구현해서 만들며 다음 세 가지 메서드가 포함되어있다.

  • preHandle - 컨트롤러 호출 전에 실행
  • postHandle - 컨트롤러 실행 후 호출. (컨트롤러의 ModelAndView 결과를 조작할 수도 있다)
  • afterCompletion - 위의 모든 작업이 다 완료된 후 호출

HandlerAdapter

특정 핸들러(컨트롤러)의 메서드를 호출하는 역할

public interface HandlerAdapter {
	
	// 어댑터가 핸들러를 지원하는지 확인한다
	boolean supports(Object handler);
	
	// 핸들러를
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

핸들러 매핑으로 선택한 핸들러를 호출할 때 사용한다. (컨트롤러마다 호출방법이 다 다르기 때문에 이게 없다면 DispatcherServlet 은 알 길이 없다)

따로 설정하지 않는다면 기본값으로는 HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, RequestMappingHandlerAdapter 3가지가 지정되어 있다.

  • HttpRequestHandlerAdapter 는 HttpRequestHandler 인터페이스의 구현체를 지원하는 어댑터이다.
  • SimpleControllerHandlerAdapter는 Controller 인터페이스의 구현체를 지원하는 어댑터이다,
  • RequestMappingHandlerAdapter는 @RequestMapping 어노테이션이 붙은 컨트롤러를 지원하는 어댑터이다.

HandlerExceptionResolver

컨트롤러 작업 중 발생한 예외를 관리하는 역할

DispatcherServlet은 예외를 적절한 HandlerExceptionResolver에 위임한다.

기본값으로는 다음 세 가지가 있다.

  • ExceptionHandlerExceptionResolver:  @ExceptionHandler 어노테이션이 붙은 메서드에서 예외를 처리하는 전략이다.
  • ResponseStatusExceptionResolver : @ResponseStatus를 특정 예외 클래스에 붙여, 해당 예외 발생시 지정한 status code와 이유를 반환할 수 있다.
  • DefaultHandlerExceptionResolver : 마지막으로 실행되는 예외 처리 전략이며 발생한 예외에 따라 특정 status code를 반환한다

ViewResolver

컨트롤러가 리턴한 뷰 이름(String)을 참고해 적절한 뷰 오브젝트를 반환한다.

뷰 종류에 따라 적절한 View Resolver를 추가로 설정할 수 있다.

참고

  • 토비의 스프링 3.1

0개의 댓글