[Spring] 디스패처 서블릿(Dispatcher-Servlet)

yb__char·2023년 11월 17일
1

일단 서블릿(Java Servlet)뭘까?

자바 서블릿(Java Servlet)은 자바를 사용하여 웹 페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말하며, 흔히 "서블릿"이라 불린다.
자바 서블릿은 웹 서버의 성능을 향상하기 위해 사용되는 자바 클래스의 일종이다.
즉, 서블릿(Servlet)은 클라이언트 요청을 처리하고, 그 결과를 반환하는 웹 프로그래밍 기술이다.


디스패처 서블릿

Dispatcher의 뜻은 보내는 사람 즉, 파견자라는 의미를 가지고 있다.
단어 의미 그대로 가장 먼저 클라이언트로부터 어떠한 요청이 오면 Tomcat(톰캣)과 같은 서블릿 컨테이너가 요청을 받게 된다. 그리고 이 모든 요청을 프론트 컨트롤러인 디스패처 서블릿이 가장 먼저 받게 된다.
그러면 디스패처 서블릿은 공통적인 작업을 우선적으로 함수 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아서 작업을 위임한다.
즉, 서블릿 컨테이너의 가장 앞단에서 HTTP 프로토콜로 들어오는 모든 요청을 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러이다.

프론트 컨트롤러란?

프론트 컨트롤러(Front Controller)란 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아서 처리해주는 컨트롤러이다. 프론트 컨트롤러가 도입되기 전에는 각 컨트롤러마다 공통 로직을 복붙 형식으로 다시 작성하여 사용했지만, 프론트 컨트롤러가 도입된 이후에는 공통의 로직에 대한 처리가 가능해졌다.


디스패처 서블릿의 장점

과거 DispatcherServlet이 등장하기 전에는 모든 서블릿에 대해 URL을 매핑하기 위해 관련 설정을 해주어야 하는 번거로움이 존재하였다고 한다.
그래서 Spring MVC는 DispatcherServlet이 등장함에 따라 web.xml의 역할을 상당히 축소시켜주었다.

과거에는 모든 서블릿을 URL 매핑을 위해 web.xml에 모두 등록해주어야 했지만, DispatcherServlet이 등장한 이후에는 DispatcherServlet이 어플리케이션으로 들어오는 모든 요청을 먼저 받아서 핸들링 해주고 공통 작업을 처리하면서 직접 적합한 컨트롤러로 위임을 해주기 때문에 위와 같은 작업은 필요없어졌고, 이로 인해 개발이 굉장히 편리해졌다고 한다.

우리는 컨트롤러를 구현해두기만 하면 디스패처 서블릿가 알아서 적합한 컨트롤러로 위임을 해주는 구조가 되었다.
그러나 개발자의 입장에서 굉장히 편리해진 만큼 DispatcherServlet의 내부 코드는 굉장히 추상화가 되어있고 복잡하다.


디스패처 서블릿의 흐름 ( Spring MVC 동작순서 )

스프링 부트에서는 스프링 부트 구동시 DispatcherServlet을 서블릿으로 자동 등록하고, 모든 경로 "/"에 대해 매핑한다.

  1. 처음 클라이언트에서 요청이 오면 디스패처 서블릿이 해당 요청을 받는다.
  2. Handler Mapping을 통해 요청에 알맞은 Controller를 찾아낸다.
  3. 요청에서 찾아낸 Controller를 Handler Adapter를 찾아 해당 Controller의 메서드를 실행시킨다.
    이후 Controller 로직 설명은 생략한다.
  4. 핸들러 어댑터가 Controller로 요청을 위임함
  5. Controller 요청을 처리한 뒤 처리한 결과와 해당 뷰 정보(ModelAndView)를 다시 디스패처 서블릿에게 전달한다.
  6. 받은 정보로 디스패처 서블릿은 View Resolver를 통해 View 파일을 찾는다.
  7. 응답이 화면이라면 View의 이름에 맞는 View를 찾아서 반환해주는 ViewResolver가 적절한 화면을 내려준다

이와 같은 흐름을 가지며, 간단하게 말하면 요청을 처리할 컨트롤러를 찾아서 위임하고 처리된 결과를 받는 것이라 생각하면 될 거 같다. 

흐름 설명

  1. 처음 클라이언트에서 요청이 오면 디스패처 서블릿이 해당 요청을 받는다.
    앞서 설명하였듯 디스패처 서블릿은 가장 먼저 요청을 받는 프론트 컨트롤러이다. 서블릿 컨텍스트(웹 컨텍스트)에서 필터들을 지나 스프링 컨텍스트에서 디스패처 서블릿이 가장 먼저 요청을 받게 된다. 이를 그림으로 표현하면 다음과 같다. 실제로는 Interceptor가 Controller로 요청을 위임하지는 않으므로, 아래의 그림은 처리 순서를 도식화한 것으로만 이해하면 된다.
  1. Handler Mapping을 통해 요청에 알맞은 Controller를 찾아낸다.
    디스패처 서블릿은 요청을 처리할 핸들러(컨트롤러)를 찾고 해당 객체의 메소드를 호출한다. 따라서 가장 먼저 어느 컨트롤러가 요청을 처리할 수 있는지를 식별해야 하는데, 해당 역할을 하는 것이 바로 HandlerMapping이다.
    최근에는 @Controller에 @RequestMapping 관련 어노테이션을 사용해 컨트롤러를 작성하는 것이 일반적이다. 하지만 예전 스펙을 따라 Controller 인터페이스를 구현하여 컨트롤러를 작성할 수도 있다. 즉, 컨트롤러를 구현하는 방법이 다양하기 때문에 스프링은 HandlerMapping 인터페이스를 만들어두고, 다양한 구현 방법에 따라 요청을 처리할 대상을 찾도록 되어 있다.
    흔한 @Controller 방식은 RequestMappingHandlerMapping가 처리한다. 이는 @Controller로 작성된 모든 컨트롤러를 찾고 파싱하여 HashMap으로 <요청 정보, 처리할 대상> 관리한다. 여기서 처리할 대상은 HandlerMethod 객체로 컨트롤러, 메소드 등을 갖고 있는데, 이는 스프링이 리플렉션을 이용해 요청을 위임하기 때문이다.
    그래서 요청이 오면 (Http Method, URI) 등을 사용해 요청 정보를 만들고, HashMap에서 요청을 처리할 대상(HandlerMethod)를 찾은 후에 HandlerExecutionChain으로 감싸서 반환한다. HandlerExecutionChain으로 감싸는 이유는 컨트롤러로 요청을 넘겨주기 전에 처리해야 하는 인터셉터 등을 포함하기 위해서이다.
  1. 요청에서 찾아낸 Controller를 Handler Adapter를 찾아 해당 Controller의 메서드를 실행시킨다.
    이후에 컨트롤러로 요청을 위임해야 하는데, 디스패처 서블릿은 컨트롤러로 요청을 직접 위임하는 것이 아니라 HandlerAdapter를 통해 위임한다. 그 이유는 앞서 설명하였듯 컨트롤러의 구현 방식이 다양하기 때문이다.
    스프링은 꽤나 오래 전에(2004년) 만들어진 프레임워크로, 트렌드를 굉장히 잘 따라간다. 프로그래밍 흐름에 맞게 스프링 역시 변화를 따라가게 되었는데, 과거에는 컨트롤러를 Controller 인터페이스로 구현하였는데, Ruby On Rails가 어노테이션 기반으로 관례를 이용한 프로그래밍을 내세워 혁신을 일으키면서 스프링 역시 이를 도입하게 되었다.
    그래서 다양하게 작성되는 컨트롤러에 대응하기 위해 스프링은 HandlerAdapter라는 어댑터 인터페이스를 통해 어댑터 패턴을 적용함으로써 컨트롤러의 구현 방식에 상관없이 요청을 위임할 수 있도록 되어있다.
  1. 핸들러 어댑터가 Controller로 요청을 위임함
    핸들러 어댑터가 컨트롤러로 요청을 위임한 전/후에 공통적인 전/후처리 과정이 필요하다. 대표적으로 인터셉터들을 포함해 요청 시에 @RequestParam, @RequestBody 등을 처리하기 위한 ArgumentResolver들과 응답 시에 ResponseEntity의 Body를 Json으로 직렬화하는 등의 처리를 하는 ReturnValueHandler 등이 핸들러 어댑터에서 처리된다. ArgumentResolver 등을 통해 파라미터가 준비되면 리플렉션을 이용해 컨트롤러로 요청을 위임한다.
  1. Controller 요청을 처리한 뒤 처리한 결과와 해당 뷰 정보(ModelAndView)를 다시 디스패처 서블릿에게 전달한다.
    HandlerAdapter는 컨트롤러로부터 받은 응답을 응답 처리기인 ReturnValueHandler가 후처리한 후에 디스패처 서블릿으로 돌려준다. 만약 컨트롤러가 ResponseEntity를 반환하면 HttpEntityMethodProcessor가 MessageConverter를 사용해 응답 객체를 직렬화하고 응답 상태(HttpStatus)를 설정한다. 만약 컨트롤러가 View 이름을 반환하면 ViewResolver를 통해 View를 반환된다.
  1. 받은 정보로 디스패처 서블릿은 View Resolver를 통해 View 파일을 찾는다.
    디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트에게 반환한다. 이때 응답이 데이터라면 그대로 반환되지만, 응답이 화면이라면 View의 이름에 맞는 View를 찾아서 반환해주는 ViewResolver가 적절한 화면을 내려준다.

이렇게 디스패처 서블릿의 정의와 흐름에 대해 알아보았다. 사실 Spring에서 필터와 인터셉터의 차이, 정의를 작성하다가 디스패처 서블릿을 먼저 알아보면 좋을 거 같아 작성하게 되었다.
간단하게 디스패처 서블릿이 있다, 이런 흐름이다라고 김영한님의 스프링 강의를 통해 알고는 있었지만, 여러 참고 블로그를 찾고 알아보다가 다시금 상기시키고 공부가 되었다.

참고

profile
안녕하세요 백엔드 개발자 차윤범입니다 :)

0개의 댓글