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

송현진·2025년 5월 31일
0

Spring Boot

목록 보기
20/23

서블릿(Java Servlet)이란?

자바를 사용하여 웹 페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말하며 흔히 "서블릿"이라 불린다. 자바 서블릿은 웹 서버의 성능을 향상 시키기 위해 사용되는 자바 클래스의 일종이다. 즉, 서블릿(Servlet)은 클라이언트 요청을 처리하고 그 결과를 반환하는 웹 프로그래밍 기술이다.

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

디스패처 서블릿의 dispatch는 "보내다"라는 뜻을 가지고 있다. 그리고 이러한 단어를 포함하는 디스패처 서블릿은 HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러(Front Controller)라고 정의할 수 있다.이것을 보다 자세히 설명하자면 클라이언트로부터 어떠한 요청이 오면 톰캣(Tomcat)과 같은 서블릿 컨테이너가 요청을 받게 된다. 그리고 이 모든 요청을 프론트 컨트롤러인 디스패처 서블릿이 가장 먼저 받게 된다. 그러면 디스패처 서블릿은 공통적인 작업을 먼저 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아서 작업을 위임한다.

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

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

Spring MVC는 디스패처 서블릿이 등장함에 따라 web.xml의 역할을 상당히 축소 시켜주었다. 과거에는 모든 서블릿을 URL 매핑을 위해 web.xml에 모두 등록해줘야 했지만 디스패처 서블릿이 해당 애플리케이션으로 들어오는 모든 요청을 핸들링해주고 공통 작업을 처리하면서 상당히 편리하게 이용할 수 있게 되었다. 우리는 컨트롤러를 구현해두기만 하면 디스패처 서블릿이 알아서 적합한 컨트롤러로 위임해주는 구조가 되었다.

정적 자원(Static Resources)의 처리

디스패처 서블릿이 요청을 컨트롤러로 넘겨주는 방식은 효율적이다. 하지만 디스패처 서블릿이 모든 요청을 처리하다보니 이미지나 HTML/CSS/JavaScript 등과 같은 정적 파일에 대한 요청마저 모두 가로채기 때문에 정적자원을 불러오지 못하는 상황도 발생하곤 했다. 이러한 문제를 해결하기 위해 개발자들은 2가지 방법을 고안했다.

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

  • /apps의 URL로 접근하면 디스패처 서블릿이 담당한다.
  • /resources의 URL로 접근하면 디스패처 서블릿이 컨트롤할 수 없으므로 담당하지 않는다.

이러한 방식은 코드가 지저분해지며 모든 요청에 대해서 저런 URL을 붙어주어야 하므로 직관적인 설계가 될 수 없다. 그래서 이러한 방법에 한계를 느끼고 다음의 방법으로 처리를 하게 되었다.

2. 애플리케이션 요청을 탐색하고 없으면 정적 자원 요청으로 처리

디스패처 서블릿이 요청을 처리할 컨트롤러를 먼저 찾고 요청에 대한 컨트롤러를 찾을 수 없는 경우 2차적으로 설정된 자원 경로를 탐색하여 자원을 탐색하는 것이다. 이렇게 영역을 분리하면 효츌적인 리소스 관리를 지원할 뿐 아니라 추후에 확장을 용이하게 해준다는 장점이 있다.

디스패처 서블릿(Dispatcher-Servlet) 동작 과정

디스패처 서블릿은 적합한 컨트롤러와 메소드를 찾아 요청을 위임해야 한다.

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

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

디스패처 서블릿은 서블릿 컨테이너 안에서 설정된 URL 패턴을 기반으로 모든 요청을 받아들인다. Spring Boot에서는 보통 "/" 패턴으로 설정되어 있으며 이는 모든 요청을 프론트 컨트롤러가 처리한다는 뜻이다. 스패처 서블릿은 필터(Filter) -> 인터셉터(Interceptor) -> 컨트롤러 흐름의 시작점에 위치하며 이 요청 흐름의 제일 앞단에서 동작한다.

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

디스패처 서블릿은 요청을 처리할 핸들러(컨트롤러)를 찾고 해당 객체의 메소드를 호출한다. 따라서 가장 먼저 어느 컨트롤러가 요청을 처리할 수 있는지를 식별해야 하는데 해당 역할을 하는 것이 바로 HandlerMapping이다. 현재는 @Controller@RequestMapping 관련 어노테이션을 사용해 컨트롤러를 작성하는 것이 일반적이다. 하지만 예전 스펙을 따라 Controller 인터페이스를 구현하여 컨트롤러를 작성할 수도 있다. 즉, 컨트롤러를 구현하는 방법이 다양하기 때문에 스프링은 HandlerMapping 인터페이스를 만들어두고 다양한 구현 방법에 따라 요청을 처리할 대상을 찾도록 되어 있다.

@Controller 방식은 RequestMappingHandlerMapping가 처리합니다. 이는 @Controller로 작성된 모든 컨트롤러를 찾고 파싱하여 HashMap으로 <요청 정보, 처리할 대상> 관리합니다. 여기서 처리할 대상은 HandlerMethod 객체로 컨트롤러, 메소드 등을 갖고 있는데 이는 스프링이 리플렉션을 이용해 요청을 위임하기 때문이다. 그래서 요청이 오면 (Http Method, URI) 등을 사용해 요청 정보를 만들고 HashMap에서 요청을 처리할 대상(HandlerMethod)를 찾은 후에 HandlerExecutionChain으로 감싸서 반환한다. HandlerExecutionChain으로 감싸는 이유는 컨트롤러로 요청을 넘겨주기 전에 처리해야 하는 인터셉터 등을 포함하기 위해서이다.

HandlerMapping은 어떤 컨트롤러가 요청을 처리할지 결정한다.

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

Handler를 찾은 다음에는 그 Handler를 실행시킬 수 있는 적절한 HandlerAdapter를 찾아야 한다. 왜냐하면 과거에는 Controller 인터페이스를 구현한 컨트롤러도 있었고 요즘은 어노테이션 기반 컨트롤러도 있기 때문에 요청을 위임하는 방식이 서로 다르다.

이때 필요한 것이 어댑터 패턴이며 스프링은 HandlerAdapter 인터페이스를 통해 컨트롤러 구현 방식과 무관하게 요청을 위임할 수 있도록 한다. 예를 들어 RequestMappingHandlerAdapter는 @RequestMapping 기반 컨트롤러를 처리하는 어댑터이고 다른 방식의 컨트롤러가 있다면 그에 맞는 어댑터가 선택된다.

HandlerAdapter는 해당 컨트롤러를 실행할 수 있는 적절한 실행기 역할을 한다.

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

핸들러 어댑터는 컨트롤러 호출 전/후로 공통 처리를 수행한다. 대표적으로 인터셉터들을 포함해 요청 시에 @RequestParam, @RequestBody 등을 처리하기 위한 ArgumentResolver들과 응답 시에 ResponseEntityBody를 Json으로 직렬화하는 등의 처리를 하는 ReturnValueHandler 등이 핸들러 어댑터에서 처리된다. ArgumentResolver 등을 통해 파라미터가 준비 되면 리플렉션을 이용해 컨트롤러로 요청을 위임한다.

ArgumentResolver, ReturnValueHandler는 파라미터 처리와 응답 직렬화를 담당한다

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

이후에 컨트롤러는 서비스를 호출하고 우리가 작성한 비즈니스 로직들이 진행된다.

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

비즈니스 로직이 처리된 후에는 컨트롤러가 반환값을 반환한다. 응답 데이터를 사용하는 경우에는 주로 ResponseEntity를 반환하게 되고 응답 페이지를 보여주는 경우라면 String으로 View의 이름을 반환할 수도 있다.

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

HandlerAdapter는 컨트롤러로부터 받은 응답을 응답 처리기인 ReturnValueHandler가 후처리한 후에 디스패처 서블릿으로 돌려준다. 만약 컨트롤러가 ResponseEntity를 반환하면 HttpEntityMethodProcessor가 MessageConverter를 사용해 응답 객체를 직렬화하고 응답 상태(HttpStatus)를 설정한다. 만약 컨트롤러가 View 이름을 반환하면 ViewResolver를 통해 View를 반환한다.

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

디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트에게 반환된다. 이때 응답이 데이터라면 그대로 반환되지만 응답이 화면이라면 View의 이름에 맞는 View를 찾아서 반환해주는 ViewResolver가 적절한 화면을 내려준다.

📝 배운점

Spring MVC의 핵심인 디스패처 서블릿은 웹 요청 처리의 진입점이자 중앙 제어 역할을 담당한다. 디스패처 서블릿 하나로 모든 요청을 관리하면서도 다양한 컨트롤러 구현 방식과 리턴 방식(데이터 응답, 뷰 렌더링 등)을 유연하게 처리할 수 있도록 HandlerMapping, HandlerAdapter, ViewResolver 등의 전략 패턴 구조로 설계된 점이 인상 깊었다. 특히, 요청 전후에 필터, 인터셉터, ArgumentResolver, ReturnValueHandler 등이 체계적으로 구성되어 있어 프레임워크 내부의 작동 원리를 이해하면 우리가 작성하는 컨트롤러 로직 외에도 스프링이 얼마나 많은 작업을 자동으로 해주는지를 알 수 있다. 이를 통해 Spring MVC의 구조적 강점과 확장성을 다시금 느낄 수 있었다. 디스패처 서블릿은 단순한 라우터가 아니라 요청-응답 흐름에서 핵심 로직을 유연하게 연결하는 중앙 허브라는 걸 알게 되었다. 결국 디스패처 서블릿은 요청을 가장 먼저 받아 적절한 컨트롤러로 위임하고 그 결과를 알맞게 가공해 응답까지 내려주는 Spring MVC의 중심축이라고 할 수 있을 거 같다.

참고

profile
개발자가 되고 싶은 취준생

0개의 댓글