2022. 8. 15. 17:42ㆍSpring
이번 글에서는 디스패처 서블릿에 대해 알아보도록 하자.
1. 핸들러 메소드(HandlerMethod)
▷ 핸들러 메소드(Handler Method)란?
핸들러 메소드는 @RequestMapping과 그 하위 어노테이션(@GetMapping, @PostMapping 등)이 붙은 메소드의 정보를 추상화한 객체이다. HandlerMethod는 그 자체가 실행 가능한 객체가 아닌, 메소드를 실행하기 위해 필요한 참조 정보를 담고 있는 객체로써 다음과 같은 정보를 가진다.
- 빈 객체
- 메소드 메타정보
- 메소드 파라미터 메타정보
- 메소드 어노테이션 메타정보
- 메소드 리턴값 메타정보
디스패처 서블릿은 애플리케이션이 실행될 때 모든 컨트롤러 빈의 메소드를 살펴서 매핑 후보가 되는 메소드들을 추출한 뒤, 이를 HandlerMethod 형태로 저장해둔다. 그러고 난 뒤, 실제 요청이 들어오면 저장해 둔 목록에서 요청 조건에 맞는 HandlerMethod를 참조해서 매핑되는 메소드를 실행한다.
▷ RequestMappingHandlerMapping (HandlerMapping 인터페이스의 구현 객체이다. 우선순위 0)
여기서 HTTP 요청을 HandlerMethod 객체로 변환하는 작업을 하거나 해당 요청에 매핑되는 HandlerMethod를 반환하는 작업은 RequestMappingHandlerMapping이 담당한다. 그래서 애플리케이션 컨텍스트가 초기화되면 RequestMappingHandlerMapping에 접근하여 저장된 매핑 정보와 핸들러 메소드 목록을 확인할 수 있다.
▷ BeanNameUrlHandlerMapping (HandlerMapping 인터페이스의 구현 객체, 우선순위 1)
스프링 빈의 이름으로 핸들러를 찾는다. 위의 방법으로 핸들러를 찾지 못할 시 이 객체 사용
2. 디스패처 서블릿(Dispatcher Servlet)이 HTTP 요청을 처리하는 방법
- 클라이언트의 요청을 디스패처 서블릿이 받는다.
- 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음
- 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함
- 핸들러 어댑터가 컨트롤러로 요청을 위임
- 비즈니스 로직을 처리함
- 컨트롤러가 반환값을 반환함
- HandlerAdapter가 반환값을 처리함
- 서버의 응답을 클라이언트로 반환
1. 클라이언트의 요청을 디스패처 서블릿이 받음
앞서 설명하였듯 디스패처 서블릿은 가장 먼저 요청을 받는 프론트 컨트롤러이다. 서블릿 컨텍스트에서 필터들을 지나 스프링 컨텍스트에서 디스패처 서블릿이 가장 먼저 요청을 받게 된다. 이를 그림으로 표현하면 다음과 같다.
2. 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음
디스패처 서블릿은 요청을 처리할 컨트롤러를 찾고 해당 메소드를 호출해야 한다. HandlerMapping의 구현체 중 하나인 RequestMappingHandlerMapping은 @Controller로 작성된 모든 컨트롤러 빈을 파싱하여 HashMap으로 (요청 정보, 처리할 대상)을 관리한다. 요청에 매핑되는 컨트롤러와 해당 메소드 등을 갖는 HandlerMethod 객체를 찾는다.
따라서 HandlerMapping은 요청이 오면 Http Method, URI 등을 사용해 Key 객체인 요청 정보를 만들고 Value인 요청을 처리할 HandlerMethod를 찾아 HandlerMethodExecutionChain으로 감싸서 반환한다. HandlerMethodExecutionChain으로 감싸는 이유는 컨트롤러로 요청을 넘겨주기 전에 처리해야 하는 인터셉터 등을 포함하기 위해서이다.
3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달
디스패처 서블릿은 컨트롤러로 요청을 직접 위임하는 것이 아니라 HandlerAdapter를 통해 컨트롤러로 요청을 위임한다. 이 때 어댑터 인터페이스를 통해 컨트롤러를 호출하는 이유는 컨트롤러의 구현 방식이 다양하기 때문이다. 최근에는 @Controller에 @RequestMapping 관련 어노테이션을 사용해 컨트롤러 클래스를 주로 작성하지만, Controller 인터페이스를 구현하여 컨트롤러 클래스를 작성할 수도 있다. 스프링을 HandlerAdapter라는 어댑터 인터페이스를 통해 어댑터 패턴을 적용함으로써 컨트롤러의 구현 방식에 상관없이 요청을 위임할 수 있다.
<핸들러 어댑터 인터페이스>
// 해당 핸들러 어댑터가 매개변수로 들어온 handler를 지원하는지 여부 (instance of 메소드 사용)
boolean supports(Object handler);
// 실제 컨트롤러를 호출하고 그 결과로 ModelView 반환,
// 실제 컨트롤러가 ModelView 반환하지 못하면 어댑터가 직접 실행 후 반환
ModelView handle(req, res, handler)
4. 핸들러 어댑터가 컨트롤러로 요청을 위임함
핸들러 어댑터가 컨트롤러로 요청을 넘기기 전에 공통적인 전/후처리 과정이 필요하다. 대표적으로 인터셉터들을 포함해 요청 시에 @RequestParam, @RequestBody 등을 처리하기 위한 ArgumentResolver들과 응답 시에 ResponseEntity의 Body를 Json으로 직렬화하는 등의 처리를 하는 ReturnValueHandler 등이 어댑터에서 컨트롤러로 전달되기 전에 처리된다. 그리고 컨트롤러의 메소드를 호출하도록 요청을 위임한다. 실제로 요청이 위임되는 과정에서 리플렉션이 사용된다.
요청을 처리할 대상 정보인 HandlerMethod 객체에는 컨트롤러 정보와 메소드 객체가 있으므로 리플렉션의 메소드 객체를 invoke 한다. 빈 팩토리에서 해당 컨트롤러 빈 객체를 찾아오고 거기에 리플렉션을 사용하게 된다.
5. 비즈니스 로직을 처리
이후에 컨트롤러는 서비스를 호출하고 개발자가 작성한 비즈니스 로직들이 실행
6. 컨트롤러가 반환값을 반환함
비즈니스 로직이 처리된 후 컨트롤러가 반환값을 반환함. 요즘 프론트엔드와 백엔드를 분리하고, MSA로 가고 있는 시대에서 주로 ResponseEntity를 반환함. 물론 컨트롤러에서 View 이름 반환도 가능
7. HandlerAdapter가 반환값을 처리함
HandlerAdapter는 컨트롤러부터 받은 응답을 응답 처리기인 ReturnValueHandler가 후처리한 후에 디스패처 서블릿으로 돌려준다. 만일 컨트롤러가 ResponseEntity를 반환한 경우 HttpEntityMethodProcessor가 MessageConverter를 사용해 응답 객체를 직렬화하고 응답 상태(HttpStatus)를 반환한다. 만일 컨트롤러가 View 이름을 반환하면 ViewResolver를 통해 View를 반환한다.
8. 서버의 응답을 클라이언트로 반환함
디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트에게 반환됨
[출처]
'Spring' 카테고리의 다른 글
[Spring] 빈 스코프의 종류 (0) | 2022.10.13 |
---|---|
[Spring] @Configuration 안에 @Bean을 사용해야 하는 이유 (0) | 2022.10.13 |
[Spring] 빈 등록을 위한 어노테이션 @Bean, @Configuration, @Component (0) | 2022.10.12 |
[Spring] @RequestParam, @RequestBody, @ModelAttribute (0) | 2022.10.12 |
[Spring] WebSocket & Spring (0) | 2022.08.17 |