Dispatcher Servlet ?
- HTTP Protocol 로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러
- 클라이언트 ▶ 톰캣 (서블릿 컨테이너) ▶ 디스패처 서블릿 (공통작업 처리) ▶ 해당 컨트롤러 (로 작업 위임)
- Dispatcher Servlet이 등장하면서, web.xml의 역할이 상당히 축소
서블릿을 URL 매핑을 위해 web.xml에 모두 등록해야 했지만, 디스패처 서블릿이 해당 어플리케이션으로 들어오는 모든 요청 핸들링, 공통 작업 처리로 편리하게 이용
컨트롤러를 구현하기만 하면 디스패처 서블릿이 적합한 컨트롤러로 알아서 위임해줌
Static Resources (정적 자원) 처리
- HTML, CSS, JavaScript, Image 등 정적 파일에 대한 요청도 처리하려고 하여, 정적 자원을 불러오지 못하는 상황이 발생
클라이언트의 요청을 두 가지로 분리
정적 자원에 대한 요청 : /src 의 URL로 접근하면 Dispatcher Servlet이 처리
어플리케이션에 대한 요청 : /resources 의 URL로 접근하면 Dispatcher Servlet이 컨트롤하지 않음
코드가 지저분해지고, 직관적이지 못해 한계를 느낌- 어플리케이션에 대한 요청을 탐색하고 없으면 정적 자원에 대한 요청으로 처리
요청에 대한 컨트롤러를 찾을 수 없는 경우, 정적 자원 처리 (후순위로 설정된 resource 경로를 탐색)
효율적인 리소스 관리 & 확장 용이
Dispatcher Servlet 의 동작 방식
- 디스패처 서블릿을 통해 적합한 컨트롤러와 메소드를 찾아 요청 위임
- 클라이언트의 요청을 디스패처 서블릿이 받음
- 요청 정보를 통해 요청을 위임할 컨트롤러 찾음
- 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달
- 핸들러 어댑터가 컨트롤러로 요청을 위임
- 비지니스 로직 처리
- 컨트롤러가 값 반환
- 핸들러 어댑터가 반환값 처리
- 서버의 응답을 클라이언트로 반환
1. 클라이언트의 요청을 디스패처 서블릿이 받음
서블릿 컨텍스트 (웹 컨텍스트) 에서 필터들을 지나 스프링 컨텍스트에서 디스패처 서블릿이 가장 먼저 요청을 받게 됨
2. 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음
디스패처 서블릿은 요청을 처리할 컨트롤러를 찾고 해당 메소드를 호출해야 합니다. HandlerMapping의 구현체 중 하나인 RequestMappingHandlerMapping은 @Controller로 작성된 모든 컨트롤러 빈을 파싱하여 HashMap으로 (요청 정보, 처리할 대상)을 관리합니다. 엄밀히는 컨트롤러가 아닌, 요청에 매핑되는 컨트롤러와 해당 메소드 등을 갖는 HandlerMethod 객체를 찾습니다.
그래서 HadlerMapping은 요청이 오면 Http Method, URI 등을 사용해 Key 객체인 요청 정보를 만들고, Value인 요청을 처리할 HandlerMethod를 찾아 HandlerMethodExecutionChain으로 감싸서 반환합니다. HandlerMethodExecutionChain으로 감싸는 이유는 컨트롤러로 요청을 넘겨주기 전에 처리해야 하는 인터셉터 등을 포함하기 위해서입니다.
3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달
디스패처 서블릿은 컨트롤러로 요청을 직접 위임하는 것이 아니라 HandlerAdapter를 통해 컨트롤러로 요청을 위임합니다. 이 때, 어댑터 인터페이스를 통해 컨트롤러를 호출하는 이유는 컨트롤러의 구현 방식이 다양하기 때문입니다. 최근에는 @Controller에 @RequestMapping 관련 어노테이션을 사용해 컨트롤러 클래스를 주로 작성하지만, Controller 인터페이스를 구현하여 컨트롤러 클래스를 작성할 수도 있습니다. 스프링은 HandlerAdapter라는 어댑터 인터페이스를 통해 어댑터 패턴을 적용함으로써 컨트롤러의 구현 방식에 상관없이 요청을 위임할 수 있는 것입니다.
4. 핸들러 어댑터가 컨트롤러로 요청을 위임
핸들러 어댑터가 컨트롤러로 요청을 넘기기 전에 공통적인 전/후처리 과정이 필요합니다. 대표적으로 인터셉터들을 포함해 요청 시에 @RequestParam, @RequestBody 등을 처리하기 위한 ArgumentResolver들과 응답 시에 ResponseEntity의 Body를 Json으로 직렬화하는 등의 처리를 하는 ReturnValueHandler 등이 어댑터에서 컨트롤러로 전달되기 전에 처리됩니다. 그리고 컨트롤러의 메소드를 호출하도록 요청을 위임합니다. 실제로 요청이 위임되는 과정에서는 리플렉션이 사용됩니다.요청을 처리할 대상 정보인 HandlerMethod 객체에는 컨트롤러 정보와 메소드 객체가 있으므로 리플렉션의 메소드 객체를 invoke 합니다. 사실 실제로는 HandlerMethod에 컨트롤러 빈 이름과 메소드, 빈 팩토리가 있어서 빈 팩토리에서 컨트롤러 빈을 찾습니다. 그리고 해당 컨트롤러 빈 객체로부터 리플렉션을 사용하는데, 그렇게 중요하지는 않음
5. 비지니스 로직을 처리
이후에 컨트롤러는 서비스를 호출하고 우리가 작성한 비지니스 로직들이 진행
6. 컨트롤러가 값 반환
비지니스 로직이 처리된 후에는 컨트롤러가 반환값을 반환합니다. 요즘 프론트엔드와 백엔드를 분리하고, MSA로 가고 있는 시대에서는 주로 ResponseEntity를 반환합니다. 물론 컨트롤러에서 View 이름을 반환할 수도 있습니다.
7. HandlerAdapter가 반환값을 처리
HandlerAdapter는 컨트롤러로부터 받은 응답을 응답 처리기인 ReturnValueHandler가 후처리한 후에 디스패처 서블릿으로 돌려줍니다. 만약 컨트롤러가 ResponseEntity를 반환하면 HttpEntityMethodProcessor가 MessageConverter를 사용해 응답 객체를 직렬화하고 응답 상태(HttpStatus)를 설정합니다. 만약 컨트롤러가 View 이름을 반환하면 ViewResolver를 통해 View를 반환합니다.
8. 서버의 응답을 클라이언트로 반환
디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트로 반환