[Spring Boot] Dispatcher Servlet

2023. 1. 13. 17:34기술 창고/Spring

728x90
SMALL

Dispatcher Servlet

Dispatcher Servlet 은 HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러라고 볼 수 있다.

클라이언트로부터 요청이 들어오면 Tomcat 과 같은 Servlet 컨테이너가 요청을 받게 된다.

그리고 이 모든 요청을 Dispatcher Servlet이 가장 먼저 받게 된다.

그러면 Dispatcher Servlet은 공통적인 작업을 먼저 처리한 이후에 해당 요청을 처리해야 하는 컨트롤러를 찾아서 위임해준다.

여기서 프론트 컨트롤러라는 용어는 Servlet 컨테이너에서 제일 앞에서 서버로 들어오는 클라이언트의 요청들을 받아서 처리해주는 컨트롤러로서 MVC 패턴 구조에서 함께 사용되는 디자인 패턴이다.

즉, 문지기의 역할이라고 보면 될 듯하다.

 

 

Dispatcher Servlet 장점

과거에는 모든 Servlet을 URL 매핑을 위해 web.xml에 모두 등록해주어야 하는 번거로움이 있었지만, Dispatcher Serlvet 이 있음으로 인해서 해당 어플리케이션으로 들어오는 HTTP 요청들을 알아서 핸들링 해주고, 공통 작업을 처리해주면서 알맞는 컨트롤러에 위임해주기 때문에 개발자의 입장에서는 컨트롤러만 명확하게 구현해두기만 한다면 Dispatcher Servlet이 알아서 해준다.

 

 

정적 자원에 대한 처리

Dispatcher Servlet이 모든 요청을 알아서 컨트롤러로 위임해주는 방식은 효율적이면서 편하지만 모든 요청을 처리하다보니 이미지나 HTML, CSS, JS 등과 같은 정적 파일에 대한 요청마저 모두 가로채기 때문에 정적 자원을 불러오지 못하는 이슈도 발생하곤 한다.

이러한 문제를 해결하기 위해 두 가지 방법이 있다.

 

 

1. 정적 자원에 대한 요청과 어플리케이션에 대한 요청 분리

예)

- /application URL로 접근하면 Dispatcher Servlet이 담당

- /resource URL로 접근하면 정적 자원에 대한 요청으로 Dispatcher Servlet이 담당하지 않는다.

 

이러한 방식으로 사용하게 되면 명확하게 Dispatcher Servlet에서 담당해야할지 구분은 할 수 있겠으나 개발을 진행하다보면 해당 URL 주소를 일일히 붙여주어야하고 코드가 더러워질 수 있다.

 

 

2. 어플리케이션에 대한 요청을 탐색, 없으면 정적 자원에 대한 요청으로 간주하여 처리

먼저 Dispatcher Servlet이 요청을 처리할 알맞은 컨트롤러를 찾고, 없으면 정적 자원 경로를 탐색하여 자원을 찾는다.

이런식으로 구분을 해놓으면 효율적으로 리소스 관리가 될 뿐만 아니라 확장성도 좋다는 장점이 있을 것이다.

 

 

 

 

Dispatcher Servlet 동작 방식

(1) 클라이언트의 요청을 Dispatcher Servlet 에서 받는다.

서블릿 컨텍스트(웹 컨텍스트)에서 filter들을 지나 스프링 컨텍스트에서 Dispatcher Servlet이 가장 먼저 요청을 받게된다.

즉, 위의 사진에는 나와있지 않지만 Dispatcher Servlet에 도달하기 이전에 filter들을 거쳐서 Dispatcher Servlet으로 도달하게 되는 것이다..

 

 

(2) 요청 정보를 위임할 알맞은 컨트롤러 찾기

Dispatcher Servlet 에 도달한 요청 정보를 가지고 해당 요청을 처리할 알맞은 컨트롤러를 찾고 그 안에 있는 메소드를 호출한다.

위의 사진에 나와있는 HandlerMapping의 구현체인 RequestMappingHandlerMapping은 @Controller로 작성된 모든 컨트롤러 Bean을 HashMap으로 (요청 정보, 처리 대상) 관리한다.

따라서 컨트롤러를 찾는 것이 아니라, 해당되는 컨트롤러 안에 있는 관련 메소드를 갖는 HandlerMethod 객체를 찾는다.

HandlerMapping은 요청이 오면 앞서 말한 HashMap으로 관리하는데, HTTP 메소드, URL 등을 사용하여 요청 정보 key 객체를 만들고, 찾은 HandlerMethod를 HandlerMethodExecutionChain으로 감싸서 value 객체를 만든다.

HandlerMethodExecutionChain으로 감싸는 이유는 컨트롤러로 넘기기 이전에 처리해야 하는 인터셉터 등을 포함하기 위해서라고 한다.

 

 

(3) 요청을 컨트롤러로 위임할 Handler Adaptor를 찾아서 전달

앞서 2번 순서에서 알맞은 컨트롤러를 찾긴 하지만 그 때 바로 컨트롤러로 위임하는 것이 아니다. 찾기만 할 뿐이다.

Dispatchet Servlet 은 컨트롤러로 요청을 직접 위임하는 것이 아니라 Handler Adaptor를 통해 컨트롤러로 요청을 위임한다.

Handler Adaptor 인터페이스를 통해 컨트롤러를 호출하는 이유는 컨트롤러 구현 방식이 다양하기 때문이다.

@Controller 어노테이션으로 컨트롤러를 명시한다음에 @RequestMapping 어노테이션을 통해 컨트롤러를 구현하는 방식이 대표적이고 흔히 사용하는 방식이다.

Spring은 Handler Adaptor를 통해 컨트롤러의 구현 방식에 상관없이 요청을 위임할 수 있다.

 

 

(4) Handler Adaptor가 컨트롤러로 요청을 위임.

Dispatcher Servlet이 공통적인 작업을 처리해주면서 요청을 컨트롤러로 넘겨준다고 말했었는데, 이 공통적인 작업이 Handler Adaptor에서 컨트롤러로 요청을 넘겨줄 때 필요하다.

대표적으로 인터셉터들을 포함해 @RequestParam, @RequestBody 등을 처리하기 위해 ArgumentResolver들과 응답 시에 ResponseEntity의 Body를 JSON 으로 직렬화하여 처리를 하는 ReturnValueHandler 등이 컨트롤러에 전달되기 이전에 처리된다.

그 이후에 컨트롤러의 메소드를 호출하도록 요청을 위임한다.

 

 

(5) 비즈니스 로직 처리

컨트롤러에 요청이 위임되고 난 이후에 해당 컨트롤러 안의 메소드가 호출, 구현한 비즈니스 로직으로 서비스가 실행이 된다.

 

 

(6) 컨트롤러가 반환값을 반환

서비스가 처리된 이후에 반환받은 반환값을 컨트롤러에서 받아서 반환한다.

(요즘과 같이 프론트엔드와 백엔드를 분리하고, MSA로 가고 있는 시대에서는 주로 ResponseEntity를 반환한다.)

 

 

(7) 클라이언트로 반환

Dispatcher Servlet을 통해 반환되는 응답은 다시 필터들을 거쳐서 클라이언트에게 반환된다.

 

 

 

Spring Dispatcher Servlet 이 동작하는 자세한 과정에 대한 내용은 정리를 잘 해주신 블로그가 있어서 참고하면 될 것같다.

https://mangkyu.tistory.com/216

 

728x90
반응형
LIST