개요
- 디스패처 서블릿에 대한 이해와, 개념에 대해 파악할 수 있습니다
디스패처 서블릿의 개념
- dispatch는 "보내다" 라는 뜻을 의미합니다
- HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아, 적합한 컨트롤러에 위임해주는 프론트 컨트롤러라고 정의할 수 있습니다
- 대략적인 흐름은 아래와 같습니다
- 클라이언트의 요청
- 톰캣(WAS)을 통해 서블릿 컨테이너로 요청이 전달
- 컨테이너 내부의 디스패처 서블릿이 요청을 받은후
- 공통적인 작업 처리후, 컨트롤러를 찾아서 작업위임
프론트 컨트롤러?
- 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아서 처리해주는 컨트롤러
- MVC 구조에서 함께 사용되는 디자인 패턴
장점
- 과거에는 모든 서블릿을 URL 매핑을 위해 Web.xml에 등록해야했지만, 현재는 디스패처 서블릿이 모든 과정을 처리해줍니다
- 우리는 컨트롤러를 구현하기만 하면, 디스패처 서블릿이 알아서 적합한 컨트롤러로 위임하는 구조가 되었습니다
정적자원(static resource)의 처리
- 디스패처 서블릿은 매우 유용합니다
- 하지만 모든 요청을 처리하다보니, HTML/CSS/JavaScript와 같은 정적 파일에 대한 요청마저 모두 가로채는 까닭에 정적자원을 불러오지 못하는 상황이 발생하였고, 이를 해결하기 위한 2가지의 해결책이 존재하였습니다
- 정적 자원 요청과 어플리케이션 요청 분리
- 애플리케이션 요청 탐색 후, 정적 자원 처리
정적 자원 요청과 애플리케이션 요청 분리
- Ex)
- /apps URL 접근시 디스패처 서블릿이 담당 O
- /resources URL 접근시 디스패처 서블릿이 담당 X
- 모든 요청에 대해서 특정한 URL을 수식어로 추가해야하는것이 직관적인 설계가 되지않아 기각
애플리케이션에서 요청을 탐색하고 없으면, 정적 자원 처리
- 디스패처 서블릿이 먼저 컨트롤러 매핑후, 존재하지 않는다면 2차적으로 설정도니 resource 경로를 활용하여 자원 탐색
- 이러한 방법으로 영역을 분리하니, 효율적인 리소스 관리를 지원할 뿐 아니라 추후 확장이 용이하다는 장점까지 있었습니다
디스패처 서블릿의 동작과정
- 디스패처 서블릿은 적합한 컨트롤러와, 메소드(Http Methods)를 찾아 요청을 위임 해야합니다
흐름은 아래와 같습니다 (원본)
- 클라이언트의 요청을 디스패처 서블릿이 받음
- 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음
- 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함
- 핸들러 어댑터가 컨트롤러로 요청을 위임함
- 비지니스 로직을 처리함
- 컨트롤러가 반환값을 반환함
- 핸들러 어댑터가 반환값을 처리함
- 서버의 응답을 클라이언트로 반환함
흐름은 아래와 같습니다 (내가 정리한것)
- 클라이언트의 요청
- 서블릿 컨테이너가 HttpServeltRequqest,Response 생성후 두 객체와 함께 디스패처 서블릿으로 넘김
- 요청정보를 넘길 컨트롤러를 찾음
- 찾았다면, 그 컨트롤러의 어댑터에 찾아서 전달
- 핸들러 어댑터가 컨트롤러에 요청 위임
- 서비스 로직 처리
- 컨트롤러 값 반환
- 핸들러 어댑터 값 반환
- 서버의 응답을 클라이언트에게 반환
1. 클라이언트의 요청을 디스패처 서블릿이 받음
- 디스패처 서블릿은 가장 먼저 요청을 받는 프론트 컨트롤러
- 서블릿 컨텍스트 (웹 컨텍스트)에서 필터들을 지나 스프링 컨텍스트에서 디스패처 서블릿이 가장 먼저 요청을 받게됩니다
- 실제로 인터셉터가 컨트롤러 요청을 위임하지는 않으므로, 순서를 도식화한 그림입니다
2. 요청 정보를 통해 위임할 컨트롤러 찾음
- 가장 먼저 어느 컨트롤러가 요청을 처리할수 있는지를 식별해야하는데, 해당 역할을 하는것이 HandlerMapping 입니다
- 오늘날 흔한 @Controller 방식은 RequestMappingHandlerMapping이 처리합니다
- 흐름은 아래와 같습니다
- Spring이 애플리케이션 컨텍스트 초기화시, @Controller, @RequestMapping이 붙은 클래스와 메서드를 모두 스캔합니다
- 이를 기반으로 요청정보(URL, HTTP 메서드)를 키로, HandlerMethod 객체를 값으로 하는 HashMap에 매핑 정보 저장
- Ex) Key: "GET /users/123" , Value: "UserController#getUserById()
- Value가 HandlerMethod 객체임, 아래와 같은 형태를 하고있는 모습
- 요청이 들어오면, RequestMappingHandlerMapping이 HashMap에서 핸들러 메서드를 찾습니다
- UserController#getUserById() 이걸 찾는다는 뜻
- HadnlerExecutionChain으로 감싼후, 컨트롤러에 넘김
- 왜 감싸나요?
- 컨트롤러 메서드와 인터셉터 정보를 포함하는 객체
- 요청 처리 전후에, 로깅, 인증/인가등을 가능하게 해줌
3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함
- 2번 과정이 수행된후, 컨트롤러로 요청을 위임할때 디스패처 서블릿이 직접 위임하는것이 아닌 핸들러 어댑터를 통해 위임하게 됩니다
- 왜 바로 위임을 안하고, 핸들러 어댑터를 통해 시키나요?
- 컨트롤러의 구현방식은 다양하다, 따라서 어떠한 컨트롤러를 구현하여도 중간 매개체인 핸들러 어댑터를 통해 통합적으로 처리하는것
- 왜 바로 위임을 안하고, 핸들러 어댑터를 통해 시키나요?
4. 핸들러 어댑터가 컨트롤러로 요청을 위임함
- 핸들러 어댑터가 컨트롤러로 요청을 위임한 전/후에 공통적인 전/후 처리가 필요합니다
- 대표적으로 인터셉터들을 포함해 요청시에 @RequestParam, @RequestBody 등을 처리하기 위한 ArgumentResolver 들과 응답시에 ResponseEntity의 Body를 Json으로 직렬화하는 등의 ReturnValueHadnler 등이 핸들러 어댑터에서 처리됩니다
- ArgumentResolver 등을 통해 파라미터가 준비되면 리플렉션을 이용해 컨트롤러로 요청을 위임 합니다
- 흐름은 아래와같다
- 클라이언트 요청
- DispatcherServlet이 요청 수신
- HandlerMapping을 통해 컨트롤러 매핑
- HandlerAdapter:
- 컨트롤러 호출 전: 인터셉터와 ArgumentResolver로 매개변수 준비
- 컨트롤러 실행 (비즈니스 로직 처리)
- HandlerAdapter:
- 컨트롤러 반환값 처리 (ReturnValueHandler, 직렬화 등)
- 응답 후 인터셉터 실행
- DispatcherServlet이 최종 응답을 클라이언트에 반환
5. 비즈니스 로직을 처리함
- 이후에 컨트롤러는 서비스를 호출하고 우리가 작성한 비즈니스 로직 실행
6. 컨트롤러가 반환값을 반환
- 비즈니스 로직이 처리된 후 컨트롤러가 반환값을 반환
- 응답 데이터를 사용하는 경우는 주로 ResponseEntity를 반환합니다
- 응답 페이지라면 Stirng으로 View 이름도 반환 가능
7. 핸들러 어댑터가 반환값을 처리
- 핸들러 어댑터는 컨트롤러로 부터 받은 값을, 응답처리기인 ReturnValueHadnler가 후처리 한후에 디스패처 서블릿으로 돌려줍니다
- 분기처리
- 컨트롤러가 ResponseEntity를 반환하는 경우
- HttpEntityMethodProcessor가 MessageConverter를 사용하여 응답 객체 직렬화, 응답상태 설정
- 컨트롤러가 View이름을 반환하는 경우
- ViewResolve를 통해 View반환
- 컨트롤러가 ResponseEntity를 반환하는 경우
8. 서버의 응답을 클라이언트로 반환함
- 디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트에게 반환됩니다
- 분기처리
- 응답 데이터
- 그대로 반환
- 응답이 화면
- View의 이름에 맞는 View를 찾아서 반환
- 응답 데이터
정리
- 디스패처 서블릿의 처리방법에 대해 나열해보자
- 클라이언트의 HTTP 요청
- 필터(전)를 거친후 이동
- 서블릿 컨테이너는 HttpServletRequest,Response를 생성후, 다음으로 이동
- 디스패처 서블릿이 이를 수신후, HandlerMapping을 통하여 컨트롤러 탐색
- 애플리케이션 초기화시 HashMap 유사구조로 저장되어 있고, HadnlerMethod를 찾는과정
- 핸들러 어댑터를 통하여 컨트롤러에게 위임
- 인터셉터(전)가 실행될수있는 객체, RequestParam,RequestBody등의 어노테이션을 역직렬화하여 넘겨준다 (ArguementResolver)
- 서비스 로직 실행후, 값 반환
- 핸들러 어댑터에 다시 전달됨
- 인터셉터(후)가 실행됨
- 반환된 값을 RetrunValueHandler를 통해 결정함
- 응답 데이터라면, stauts등 설정하여 반환 (ResponseEntity 이기때문에)
- 응답 페이지라면, View를 찾아서 반환
- 필터(후) 실행
- 클라이언트에게 값 반환
참조 문헌
'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 |