SpringBoot

디스패처 서블릿(Dispatcher-Servlet) ?

Terror123 2025. 1. 20. 15:07

개요

  • 디스패처 서블릿에 대한 이해와, 개념에 대해 파악할 수 있습니다

디스패처 서블릿의 개념

  • dispatch는 "보내다" 라는 뜻을 의미합니다
  • HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아, 적합한 컨트롤러에 위임해주는 프론트 컨트롤러라고 정의할 수 있습니다
  • 대략적인 흐름은 아래와 같습니다
    1. 클라이언트의 요청
    2. 톰캣(WAS)을 통해 서블릿 컨테이너로 요청이 전달
    3. 컨테이너 내부의 디스패처 서블릿이 요청을 받은후
    4. 공통적인 작업 처리후, 컨트롤러를 찾아서 작업위임

      프론트 컨트롤러?

      • 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아서 처리해주는 컨트롤러
      • MVC 구조에서 함께 사용되는 디자인 패턴


장점

  • 과거에는 모든 서블릿을 URL 매핑을 위해 Web.xml에 등록해야했지만, 현재는 디스패처 서블릿이 모든 과정을 처리해줍니다
  • 우리는 컨트롤러를 구현하기만 하면, 디스패처 서블릿이 알아서 적합한 컨트롤러로 위임하는 구조가 되었습니다

정적자원(static resource)의 처리

  • 디스패처 서블릿은 매우 유용합니다
  • 하지만 모든 요청을 처리하다보니, HTML/CSS/JavaScript와 같은 정적 파일에 대한 요청마저 모두 가로채는 까닭에 정적자원을 불러오지 못하는 상황이 발생하였고, 이를 해결하기 위한 2가지의 해결책이 존재하였습니다
    • 정적 자원 요청과 어플리케이션 요청 분리
    • 애플리케이션 요청 탐색 후, 정적 자원 처리

정적 자원 요청과 애플리케이션 요청 분리

  • Ex)
    • /apps URL 접근시 디스패처 서블릿이 담당 O
    • /resources URL 접근시 디스패처 서블릿이 담당 X
  • 모든 요청에 대해서 특정한 URL을 수식어로 추가해야하는것이 직관적인 설계가 되지않아 기각

애플리케이션에서 요청을 탐색하고 없으면, 정적 자원 처리

  • 디스패처 서블릿이 먼저 컨트롤러 매핑후, 존재하지 않는다면 2차적으로 설정도니 resource 경로를 활용하여 자원 탐색
  • 이러한 방법으로 영역을 분리하니, 효율적인 리소스 관리를 지원할 뿐 아니라 추후 확장이 용이하다는 장점까지 있었습니다

디스패처 서블릿의 동작과정

  • 디스패처 서블릿은 적합한 컨트롤러와, 메소드(Http Methods)를 찾아 요청을 위임 해야합니다

흐름은 아래와 같습니다 (원본)

  1. 클라이언트의 요청을 디스패처 서블릿이 받음
  2. 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음
  3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함
  4. 핸들러 어댑터가 컨트롤러로 요청을 위임함
  5. 비지니스 로직을 처리함
  6. 컨트롤러가 반환값을 반환함
  7. 핸들러 어댑터가 반환값을 처리함
  8. 서버의 응답을 클라이언트로 반환함

흐름은 아래와 같습니다 (내가 정리한것)

  1. 클라이언트의 요청
  2. 서블릿 컨테이너가 HttpServeltRequqest,Response 생성후 두 객체와 함께 디스패처 서블릿으로 넘김
  3. 요청정보를 넘길 컨트롤러를 찾음
  4. 찾았다면, 그 컨트롤러의 어댑터에 찾아서 전달
  5. 핸들러 어댑터가 컨트롤러에 요청 위임
  6. 서비스 로직 처리
  7. 컨트롤러 값 반환
  8. 핸들러 어댑터 값 반환
  9. 서버의 응답을 클라이언트에게 반환

1. 클라이언트의 요청을 디스패처 서블릿이 받음

  • 디스패처 서블릿은 가장 먼저 요청을 받는 프론트 컨트롤러
  • 서블릿 컨텍스트 (웹 컨텍스트)에서 필터들을 지나 스프링 컨텍스트에서 디스패처 서블릿이 가장 먼저 요청을 받게됩니다
  • 실제로 인터셉터가 컨트롤러 요청을 위임하지는 않으므로, 순서를 도식화한 그림입니다

2. 요청 정보를 통해 위임할 컨트롤러 찾음

  • 가장 먼저 어느 컨트롤러가 요청을 처리할수 있는지를 식별해야하는데, 해당 역할을 하는것이 HandlerMapping 입니다
  • 오늘날 흔한 @Controller 방식은 RequestMappingHandlerMapping이 처리합니다
  • 흐름은 아래와 같습니다
    1. Spring이 애플리케이션 컨텍스트 초기화시, @Controller, @RequestMapping이 붙은 클래스와 메서드를 모두 스캔합니다
    2. 이를 기반으로 요청정보(URL, HTTP 메서드)를 키로, HandlerMethod 객체를 값으로 하는 HashMap에 매핑 정보 저장
      • Ex) Key: "GET /users/123" , Value: "UserController#getUserById()
      • Value가 HandlerMethod 객체임, 아래와 같은 형태를 하고있는 모습
      1. 요청이 들어오면, RequestMappingHandlerMapping이 HashMap에서 핸들러 메서드를 찾습니다
      • UserController#getUserById() 이걸 찾는다는 뜻
      1. HadnlerExecutionChain으로 감싼후, 컨트롤러에 넘김
      • 왜 감싸나요?
        • 컨트롤러 메서드와 인터셉터 정보를 포함하는 객체
        • 요청 처리 전후에, 로깅, 인증/인가등을 가능하게 해줌

3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함

  • 2번 과정이 수행된후, 컨트롤러로 요청을 위임할때 디스패처 서블릿이 직접 위임하는것이 아닌 핸들러 어댑터를 통해 위임하게 됩니다
    • 왜 바로 위임을 안하고, 핸들러 어댑터를 통해 시키나요?
      • 컨트롤러의 구현방식은 다양하다, 따라서 어떠한 컨트롤러를 구현하여도 중간 매개체인 핸들러 어댑터를 통해 통합적으로 처리하는것

4. 핸들러 어댑터가 컨트롤러로 요청을 위임함

  • 핸들러 어댑터가 컨트롤러로 요청을 위임한 전/후에 공통적인 전/후 처리가 필요합니다
  • 대표적으로 인터셉터들을 포함해 요청시에 @RequestParam, @RequestBody 등을 처리하기 위한 ArgumentResolver 들과 응답시에 ResponseEntity의 Body를 Json으로 직렬화하는 등의 ReturnValueHadnler 등이 핸들러 어댑터에서 처리됩니다
  • ArgumentResolver 등을 통해 파라미터가 준비되면 리플렉션을 이용해 컨트롤러로 요청을 위임 합니다
  • 흐름은 아래와같다
    1. 클라이언트 요청
    2. DispatcherServlet이 요청 수신
    3. HandlerMapping을 통해 컨트롤러 매핑
    4. HandlerAdapter:
      • 컨트롤러 호출 전: 인터셉터와 ArgumentResolver로 매개변수 준비
    5. 컨트롤러 실행 (비즈니스 로직 처리)
    6. HandlerAdapter:
      • 컨트롤러 반환값 처리 (ReturnValueHandler, 직렬화 등)
      • 응답 후 인터셉터 실행
    7. DispatcherServlet이 최종 응답을 클라이언트에 반환

5. 비즈니스 로직을 처리함

  • 이후에 컨트롤러는 서비스를 호출하고 우리가 작성한 비즈니스 로직 실행

6. 컨트롤러가 반환값을 반환

  • 비즈니스 로직이 처리된 후 컨트롤러가 반환값을 반환
  • 응답 데이터를 사용하는 경우는 주로 ResponseEntity를 반환합니다
  • 응답 페이지라면 Stirng으로 View 이름도 반환 가능

7. 핸들러 어댑터가 반환값을 처리

  • 핸들러 어댑터는 컨트롤러로 부터 받은 값을, 응답처리기인 ReturnValueHadnler가 후처리 한후에 디스패처 서블릿으로 돌려줍니다
  • 분기처리
    • 컨트롤러가 ResponseEntity를 반환하는 경우
      • HttpEntityMethodProcessor가 MessageConverter를 사용하여 응답 객체 직렬화, 응답상태 설정
    • 컨트롤러가 View이름을 반환하는 경우
      • ViewResolve를 통해 View반환

8. 서버의 응답을 클라이언트로 반환함

  • 디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트에게 반환됩니다
  • 분기처리
    • 응답 데이터
      • 그대로 반환
    • 응답이 화면
      • View의 이름에 맞는 View를 찾아서 반환

정리

  • 디스패처 서블릿의 처리방법에 대해 나열해보자
    1. 클라이언트의 HTTP 요청
    2. 필터(전)를 거친후 이동
    3. 서블릿 컨테이너는 HttpServletRequest,Response를 생성후, 다음으로 이동
    4. 디스패처 서블릿이 이를 수신후, HandlerMapping을 통하여 컨트롤러 탐색
      • 애플리케이션 초기화시 HashMap 유사구조로 저장되어 있고, HadnlerMethod를 찾는과정
    5. 핸들러 어댑터를 통하여 컨트롤러에게 위임
      • 인터셉터(전)가 실행될수있는 객체, RequestParam,RequestBody등의 어노테이션을 역직렬화하여 넘겨준다 (ArguementResolver)
    6. 서비스 로직 실행후, 값 반환
    7. 핸들러 어댑터에 다시 전달됨
      • 인터셉터(후)가 실행됨
      • 반환된 값을 RetrunValueHandler를 통해 결정함
        • 응답 데이터라면, stauts등 설정하여 반환 (ResponseEntity 이기때문에)
        • 응답 페이지라면, View를 찾아서 반환
    8. 필터(후) 실행
    9. 클라이언트에게 값 반환

참조 문헌

https://mangkyu.tistory.com/18

'SpringBoot' 카테고리의 다른 글

다양한 상황에서의 DB에 저장하는 시간을 알아보자  (0) 2025.02.26
Redis에 엔티티 저장중 생긴 순환참조문제  (0) 2025.01.23
JSP 란?  (1) 2025.01.21
JPA Cascade ?  (2) 2025.01.20
서블릿 이란?  (1) 2025.01.20