본문 바로가기

Spring

[Spring MVC] DispatcherServlet 의 동작 흐름

DispatcherServlet

DispatcherServlet은 Spring MVC 에서 프론트 컨트롤러 역할을 하는 서블릿 객체이다. 스프링 부트 어플리케이션을 시작하면 DispatcherServlet 객체를 생성해 서블릿 컨테이너에 등록하고, urlPatterns="/" 로 설정해 모든 요청에 대해 맵핑 한다. 때문에 서버로 요청이 들어오면 DispatcherServlet가 모든 요청의 url을 분석해 그 url에 매핑된 핸들러(컨트롤러)를 찾아 실행시킨다.

HandlerMapping

HandlerMapping은 요청 URL에 매핑된 핸들러를 찾아 조회하고 반환하는 기능을 제공하기 위한 인터페이스이다. 아래 의 메소드는 DispatcherServlet 에서 요청에 대한 핸들러(컨트롤러)를 찾기 위해서 실행되는 getHandler 메소드이다.

DispatcherServlet 에서 실행 되는 getHandler 메소드

반복문 안을 보면 HandlerMapping 객체들 중에서 특정 HTTP 요청의 URL에 매핑된 핸들러를 찾아 반환한다. 아래는 핸들러를 반환 받는 코드이다.

주석을 보면 현재 요청에 대해 핸들러를 알아낸다고 써져있다


HandlerAdapter

위에서 찾은 핸들러를 실행시키기위해서 핸들러에 맞는 HandlerAdapter 구현객체를 찾아야 한다. HandlerAdapter 객체들은 suports() 메소드를 구현하고 있는데 이를 통해 아래 코드와 같이 어댑터를 찾는다.

핸들러에 맞는 HandlerAdapter를 찾는 getHandlerAdapter 메소드

참고로 @RequestMapping 을 통해 만들어진 핸들러들은 RequestMappingHandlerMapping 에 맵핑되어있고, RequestMappingHandlerAdapter 에 의해서 실행된다. 이렇게 찾은 어댑터 객체의 handle() 메소드를 실행하면, 내부적으로 핸들러를 실행시키고 ModelAndView 객체를 반환한다.

핸들러와 핸들러 어댑터

Spring의 핸들러는 개발자가 더 편하게 비지니스 로직에 집중할 수 있는 방향으로 발전 해왔다. 현재는 대부분 @RequestMapping 을 통해 핸들러를 매핑하지만, 과거에는 스프링 빈 이름으로 url을 매핑 하는 등 다양한 방식들을 사용했다.

새로운 핸들러가 등장할 때마다 스프링의 프론트 컨트롤러인 DispatcherServlet을 수정할 수는 없기 때문에, 어댑터 패턴으로 구현하여 수정없이 확장 가능하도록 했다. 때문에 새로운 형태의 핸들러를 만들면 이 핸들러를 실행시킬 수 있는 HandlerAdapter 구현클래스를 개발하고 등록한다. DispatcherServlet은 초기화될 때, 스프링 컨테이너에서 등록된 HandlerAdapter 객체를 모두 찾아 리스트에 저장해 놓고, HTTP 요청이 들어오면 url과 매핑된 핸들러에 따라 적절한 HandlerAdapter 구현체를 찾아 핸들러를 실행시킨다.

DispatcherServlet에서 HandlerAdapter 리스트를 초기화하는 메서드 


ViewResolver

ViewResolver는 ModelAndView 담긴 렌더링할 뷰의 이름을 통해 실제 뷰를 찾는 역할을 한다. 아래는 ViewResolver 의 resolveViewName() 메소드에 viewName을 파라미터로 넘겨 뷰를 찾아 리턴하는 메소드이다.

view의 이름으로 view 객체를 반환하는코드

이렇게 반환 받은 view를 통해 아래 코드처럼 render() 메소드를 호출하고 뷰를 렌더링한다.

DispatcherSerlvet의 doDispatch() 메소드에서 뷰 객체를 반환받아 렌더링하는 코드

정리

위에 내용을 간단하게 정리하고 마무리 하겠다.

HTTP 요청이 발생하면 서블릿 컨테이너에 의해 매핑된 DispatcherServlet를 찾는다. 그리고 DispatcherServlet의 부모인 HttpServlet의 service를 오버라이드한 메소드가 실행된다. 이 메소드는 결국 doDispatch 메소드를 실행시키는데 이 메소드는 가장 먼저 HandlerMapping 을 통해 URL에 매핑된 핸들러(컨트롤러)를 찾는다. 찾은 핸들러는 특정 핸들러어댑터만이 실행시킬 수 있기 때문에, 핸들러 어댑터 목록에서 적절한 어댑터를 조회한다. 어댑터를 찾으면 어댑터의 handle() 메소드를 실행시키는데 이 메소드의 파라미터로 핸들러를 넘겨 어댑터 내부에서 핸들러를 호출한다. 핸들러의 호출이 끝나면 결과에 따라 ModelAndView 객체를 만들어 DispatcherSerlvet으로 반환한다. 이 ModelAndView 객체에는 렌더링에 필요한 데이터 모델과, 뷰의 이름을 담고있다. 이 뷰의 이름으로 ViewResolver를 통해 렌더링할 View 객체를를 얻고마지막으로 렌더링한다. 렌더링된 코드는 HTTP 응담 메세지에 담겨 클라이언트에게로 보내지게될 것이다.