Spring에서 사용자 요청 처리 흐름 + Tomcat과 DispatcherServlet 시점

Jang990·2023년 2월 16일
0
post-thumbnail

개인적으로 공부한 내용을 정리한 글이다 보니 틀린 내용이 많이 있을 수 있습니다. 틀린 내용을 지적해주세요...

Spring에서 사용자 요청 처리 흐름

사용자가 서버에 요청을 했을 때의 흐름을 간단하게 살펴보면 다음과 같습니다.

  1. 클라이언트 - 클라이언트는 서버에 요청을 보냅니다.
  2. Tomcat - Tomcat에 서블릿 컨테이너에서 요청을 처리할 적절한 서블릿을 찾습니다. 요청을 처리할 적절한 서블릿인 DispatcherServlet(Spring 애플리케이션의 일부)에게 처리를 위임합니다.
  3. Spring - DispatcherServlet에서 해당 요청을 처리하고 처리 결과를 서블릿 컨테이너로 반환합니다.
  4. Tomcat - 클라이언트에게 DispatcherServlet에서 처리한 결과를 그대로 반환합니다.
  5. 클라이언트 - 서버로 온 응답을 확인합니다.


이미지 출처: Tomcat, Spring MVC의 동작 과정


👀 Tomcat 시점

아파치 톰캣(Apache Tomcat)은 아파치 소프트웨어 재단에서 개발한 웹 애플리케이션 서버입니다.

아파치 톰캣은 웹 서버와 연동하여 실행할 수 있는 자바 환경을 제공하여 자바서버 페이지(JSP)와 자바 서블릿이 실행할 수 있는 환경을 제공하고 있습니다.

이 글에서 톰캣은 아파치 톰캣을 의미합니다.

이 부분에서는 사용자 요청이 들어왔을 때 톰캣이 어떻게 사용자의 요청을 처리하는지 확인해보겠습니다.


세부 흐름

다시 흐름으로 돌아와서 Tomcat의 세부 동작의 흐름을 보자면 다음과 같습니다.


-이미지 출처: Apache? Tomcat?? 둘이 무슨 차이지?

  1. Spring 애플리케이션이 Tomcat에 배포됩니다.
  2. Tomcat은 들어오는 요청을 처리하기 위해 스레드 풀을 만듭니다.
  3. Client가 Spring 애플리케이션에 요청을 보냅니다.
  4. Tomcat의 웹 서버에 사용자의 요청을 받고, Tomcat에 스레드 풀에 있는 스레드로 요청을 처리하기 시작합니다.
  5. Tomcat의 웹 서버는 적절한 서블릿 컨테이너에 요청을 위임합니다.
  6. 서블릿 컨테이너는 요청을 처리할 적절한 서블릿을 찾습니다.(Spring의 DispatcherServlet)
  7. 서블릿 컨테이너는 DispatcherServlet의 인스턴스를 생성합니다.
  8. Spring의 DispatcherServlet은 요청을 처리하고 응답을 반환합니다. DispatcherServlet은 요청을 처리하고 응답을 생성하기 위해 Spring 구성 요소를 사용합니다. (이는 DispatcherServlet부분에서 설명하겠습니다)
  9. Tomcat의 웹 서버는 요청한 사용자에게 응답을 보냅니다.
  10. 요청에 사용한 스레드를 스레드 풀로 반환합니다.

이 과정에서 Tomcat은 스레드 풀의 스레드를 사용하여 여러 요청을 동시에 처리합니다.

서버를 실행한 후 첫 요청에서 DispatcherServlet 인스턴스를 생성한 합니다. 각각의 스레드마다 해당 서블릿을 다시 생성하는 것이 아닙니다. 하나의 인스턴스를 각각의 스레드에서 공유하며 재사용합니다.

추가적으로 첫 요청에서 인스턴스를 생성하는 것이 아닌, 서버를 시작하면서 인스턴스를 생성하는 방법도 있으니 참고하시기 바랍니다.


스레드 풀-커넥션 풀, 서블릿에 대한 내용은 다른 글에서 다루도록 하겠습니다.


👀 Spring 시점

이 부분에서는 톰캣 시점에서 8번 단계인 Spring의 DispatcherServlet이 사용자의 요청을 어떻게 처리하는지 자세히 살펴보겠습니다.

  1. Spring의 DispatcherServlet은 요청을 처리하고 응답을 반환합니다. DispatcherServlet은 요청을 처리하고 응답을 생성하기 위해 Spring 구성 요소를 사용합니다. (이는 DispatcherServlet부분에서 설명하겠습니다)

DispatcherServlet

SpringMVC에서 제공해 주고 있는 DispatcherServlet은 "FrameworkServlet.java > HttpServlet.java > Servlet.java"를 상속받아 구현한 서블릿입니다. DispatcherServlet의 코드를 확인하고 싶다면 이곳을 확인하세요.

public class DispatcherServlet extends FrameworkServlet {
	...
}

public abstract class FrameworkServlet extends HttpServletBean {
	...
}

public abstract class HttpServletBean extends HttpServlet {
	...
}

세부 흐름

아래 그림은 스프링 MVC의 핵심 구성 요소와 각 요소 간의 관계를 정리한 것입니다.


이미지 출처 - https://jaehoney.tistory.com/47

이제 DispatcherServlet의 세부 흐름을 그림을 보면서 확인해보겠습니다. 그림에서는 웹 브라우저라고 단순화 시켜서 표현했지만, 앞에서 본 내용처럼 톰캣을 거쳐서 요청이 들어온다고 생각하면 되겠습니다.

  1. 요청이 들어옵니다.
  2. DispatcherServlet은 그 요청을 처리하기 위한 컨트롤러 객체를 검색합니다.
    DispatcherServlet이 직접 검색하지 않고, HandlerMapping이라는 빈 객체에게 컨트롤러 검색 요청합니다.
  3. HandlerMapping은 클라이언트의 요청 경로를 이용해 이를 처리할 컨트롤러 빈 객체를 DispatcherServlet에 전달합니다.
    예를 들어 웹 요청/hello라면 등록된 컨트롤러 빈 중에 /hello요청 경로를 처리할 컨트롤러를 리턴합니다.
  4. DispatcherServletHandlerMapping이 찾아준 컨트롤러 객체를 처리할 수 있는 HandlerAdapter 빈에게 요청 처리를 위임합니다.(그림 3번 과정)
    컨트롤러 객체를 DispatcherServlet이 전달받았다고 해서 바로 컨트롤러 객체의 메서드를 실행할 수 있는 것은 아닙니다. @Controller, Controller 인터페이스, HttpRequestHandler 인터페이스를 동일한 방식으로 처리하기 위해 중간에 사용되는 것이 바로 HandlerAdapter 빈입니다.
  5. HandlerAdapter는 컨트롤러의 알맞은 메서드를 호출해서 요청을 처리합니다(그림 4~5번 과정)
  6. 처리 결과를 ModelAndView라는 객체로 변환해서 DispatcherServlet에 리턴합니다.
  7. HandlerAdapter로부터 컨트롤러의 요청 처리 결과를 ModelAndView로 받으면 DispatcherServlet은 결과를 보여줄 뷰를 찾기 위해 ViewResolver 빈 객체를 사용합니다.(그림 7번 과정)
  8. ModelAndView는 컨트롤러가 리턴한 뷰 이름을 담고 있는데 ViewResolver는 이 뷰 이름에 해당하는 View 객체를 찾거나 생성해서 리턴합니다.
  9. DispatcherServletViewResolver가 리턴한 View 객체에 응답 결과 생성을 요청합니다.(그림 8번 과정)
  10. JSP를 사용하는 경우 View 객체는 JSP 를 실행함으로써 웹 브라우저에 전송할 응답 결과를 생성하고 이로써 모든 과정이 끝납니다.

클라이언트의 요청을 실제로 처리하는 것은 컨트롤러이고 DispatcherServlet은 클라이언트의 요청을 전달받는 창구 역할을 합니다.

ViewResolver, HandlerAdapter 등의 Bean은 어디서 나왔을까?

Controller는 개발자가 직접 구현했다고 하지만 ViewResolver, HandlerAdapter 등의 Bean은 어디서 나온 것일까요?

해당 Bean은 Spring 프레임워크에서 제공하는 Bean입니다. DispatcherServlet은 요청을 처리하고 적절한 응답을 렌더링하기 위해 특수 빈(special beans)에 처리를 위임합니다. 기본적으로 설정되어 제공됩니다. 하지만 개발자가 재정의하거나 교체가 가능합니다.

특수 빈(special beans)이 더 궁금하다면 이곳을 참고하시기 바랍니다.

왜 컨트롤러를 찾는데 HandlerMapping일까?

  1. DispatcherServlet은 그 요청을 처리하기 위한 컨트롤러 객체를 검색합니다.
    DispatcherServlet이 직접 검색하지 않고, HandlerMapping이라는 빈 객체에게 컨트롤러 검색 요청합니다.

2번 과정에서DispatcherServlet은 클라이언트의 요청을 처리할 컨트롤러를 찾기 위해 HandlerMapping을 사용합니다. 컨트롤러를 찾아주는 객체는 ControllerMapping 타입이여야 할 것 같은데 실제는 HandlerMapping입니다. 왜 HandlerMapping일까요?

스프링 MVC는 웹 요청을 처리할 수 있는 범용 프레임워크입니다. DispatcherServlet 입장에서는 클라이언트 요청을 처리하는 객체의 타입이 반드시 @Controller를 적용한 클래스일 필요는 없습니다.(자신이 직접 만튼 클래스를 이용해 클라이언트의 요청도 처리 가능합니다.) 실제로 클라이언트의 요청을 처리하기 위해 제공하는 타입 중에는 HttpRequestHandler도 존재합니다.

이런 이유로 스프링 MVC는 웹 요청을 실제로 처리하는 객체를 핸들러 (Handler)라고 표현하고 있으며 @Controller 적용 객체나 Controller 인터페이스를 구현하는 객체는 모두 스프링 MVC 입장에서는 핸들러가 됩니다.


출처

도서 - 초보 웹 개발자를 위한 스프링5 프로그래밍 입문
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc
Tomcat, Spring MVC의 동작 과정
Spring - 스프링 MVC 프레임워크 동작 방식
[Spring] 📚 Dispatcher Servlet 이해하기
Apache? Tomcat?? 둘이 무슨 차이지?

profile
공부한 내용을 적지 말고 이해한 내용을 설명하자

0개의 댓글