개인적으로 공부한 내용을 정리한 글이다 보니 틀린 내용이 많이 있을 수 있습니다. 틀린 내용을 지적해주세요...
사용자가 서버에 요청을 했을 때의 흐름을 간단하게 살펴보면 다음과 같습니다.
- 클라이언트 - 클라이언트는 서버에 요청을 보냅니다.
- Tomcat - Tomcat에 서블릿 컨테이너에서 요청을 처리할 적절한 서블릿을 찾습니다. 요청을 처리할 적절한 서블릿인
DispatcherServlet
(Spring 애플리케이션의 일부)에게 처리를 위임합니다.- Spring -
DispatcherServlet
에서 해당 요청을 처리하고 처리 결과를 서블릿 컨테이너로 반환합니다.- Tomcat - 클라이언트에게
DispatcherServlet
에서 처리한 결과를 그대로 반환합니다.- 클라이언트 - 서버로 온 응답을 확인합니다.
이미지 출처: Tomcat, Spring MVC의 동작 과정
아파치 톰캣(Apache Tomcat)은 아파치 소프트웨어 재단에서 개발한 웹 애플리케이션 서버입니다.
아파치 톰캣은 웹 서버와 연동하여 실행할 수 있는 자바 환경을 제공하여 자바서버 페이지(JSP)와 자바 서블릿이 실행할 수 있는 환경을 제공하고 있습니다.
이 글에서 톰캣은 아파치 톰캣을 의미합니다.
이 부분에서는 사용자 요청이 들어왔을 때 톰캣이 어떻게 사용자의 요청을 처리하는지 확인해보겠습니다.
다시 흐름으로 돌아와서 Tomcat의 세부 동작의 흐름을 보자면 다음과 같습니다.
-이미지 출처: Apache? Tomcat?? 둘이 무슨 차이지?
- Spring 애플리케이션이 Tomcat에 배포됩니다.
- Tomcat은 들어오는 요청을 처리하기 위해 스레드 풀을 만듭니다.
- Client가 Spring 애플리케이션에 요청을 보냅니다.
- Tomcat의 웹 서버에 사용자의 요청을 받고, Tomcat에 스레드 풀에 있는 스레드로 요청을 처리하기 시작합니다.
- Tomcat의 웹 서버는 적절한 서블릿 컨테이너에 요청을 위임합니다.
- 서블릿 컨테이너는 요청을 처리할 적절한 서블릿을 찾습니다.(Spring의
DispatcherServlet
)- 서블릿 컨테이너는
DispatcherServlet
의 인스턴스를 생성합니다.- Spring의
DispatcherServlet
은 요청을 처리하고 응답을 반환합니다.DispatcherServlet
은 요청을 처리하고 응답을 생성하기 위해 Spring 구성 요소를 사용합니다. (이는DispatcherServlet
부분에서 설명하겠습니다)- Tomcat의 웹 서버는 요청한 사용자에게 응답을 보냅니다.
- 요청에 사용한 스레드를 스레드 풀로 반환합니다.
이 과정에서 Tomcat은 스레드 풀의 스레드를 사용하여 여러 요청을 동시에 처리합니다.
서버를 실행한 후 첫 요청에서 DispatcherServlet
인스턴스를 생성한 합니다. 각각의 스레드마다 해당 서블릿을 다시 생성하는 것이 아닙니다. 하나의 인스턴스를 각각의 스레드에서 공유하며 재사용합니다.
추가적으로 첫 요청에서 인스턴스를 생성하는 것이 아닌, 서버를 시작하면서 인스턴스를 생성하는 방법도 있으니 참고하시기 바랍니다.
스레드 풀-커넥션 풀, 서블릿에 대한 내용은 다른 글에서 다루도록 하겠습니다.
이 부분에서는 톰캣 시점에서 8번 단계인 Spring의 DispatcherServlet
이 사용자의 요청을 어떻게 처리하는지 자세히 살펴보겠습니다.
- Spring의
DispatcherServlet
은 요청을 처리하고 응답을 반환합니다.DispatcherServlet
은 요청을 처리하고 응답을 생성하기 위해 Spring 구성 요소를 사용합니다. (이는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
의 세부 흐름을 그림을 보면서 확인해보겠습니다. 그림에서는 웹 브라우저라고 단순화 시켜서 표현했지만, 앞에서 본 내용처럼 톰캣을 거쳐서 요청이 들어온다고 생각하면 되겠습니다.
- 요청이 들어옵니다.
DispatcherServlet
은 그 요청을 처리하기 위한 컨트롤러 객체를 검색합니다.
DispatcherServlet
이 직접 검색하지 않고,HandlerMapping
이라는 빈 객체에게 컨트롤러 검색 요청합니다.HandlerMapping
은 클라이언트의 요청 경로를 이용해 이를 처리할 컨트롤러 빈 객체를DispatcherServlet
에 전달합니다.
예를 들어 웹 요청/hello
라면 등록된 컨트롤러 빈 중에/hello
요청 경로를 처리할 컨트롤러를 리턴합니다.DispatcherServlet
은HandlerMapping
이 찾아준 컨트롤러 객체를 처리할 수 있는HandlerAdapter
빈에게 요청 처리를 위임합니다.(그림 3번 과정)
컨트롤러 객체를DispatcherServlet
이 전달받았다고 해서 바로 컨트롤러 객체의 메서드를 실행할 수 있는 것은 아닙니다.@Controller
,Controller
인터페이스,HttpRequestHandler
인터페이스를 동일한 방식으로 처리하기 위해 중간에 사용되는 것이 바로HandlerAdapter
빈입니다.HandlerAdapter
는 컨트롤러의 알맞은 메서드를 호출해서 요청을 처리합니다(그림 4~5번 과정)- 처리 결과를
ModelAndView
라는 객체로 변환해서DispatcherServlet
에 리턴합니다.HandlerAdapter
로부터 컨트롤러의 요청 처리 결과를ModelAndView
로 받으면DispatcherServlet
은 결과를 보여줄 뷰를 찾기 위해ViewResolver
빈 객체를 사용합니다.(그림 7번 과정)ModelAndView
는 컨트롤러가 리턴한 뷰 이름을 담고 있는데ViewResolver
는 이 뷰 이름에 해당하는View
객체를 찾거나 생성해서 리턴합니다.DispatcherServlet
은ViewResolver
가 리턴한View
객체에 응답 결과 생성을 요청합니다.(그림 8번 과정)- JSP를 사용하는 경우
View
객체는 JSP 를 실행함으로써 웹 브라우저에 전송할 응답 결과를 생성하고 이로써 모든 과정이 끝납니다.
클라이언트의 요청을 실제로 처리하는 것은 컨트롤러이고 DispatcherServlet
은 클라이언트의 요청을 전달받는 창구 역할을 합니다.
Controller
는 개발자가 직접 구현했다고 하지만 ViewResolver
, HandlerAdapter
등의 Bean은 어디서 나온 것일까요?
해당 Bean은 Spring 프레임워크에서 제공하는 Bean입니다. DispatcherServlet
은 요청을 처리하고 적절한 응답을 렌더링하기 위해 특수 빈(special beans
)에 처리를 위임합니다. 기본적으로 설정되어 제공됩니다. 하지만 개발자가 재정의하거나 교체가 가능합니다.
특수 빈(special beans
)이 더 궁금하다면 이곳을 참고하시기 바랍니다.
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?? 둘이 무슨 차이지?