Spring MVC 3: 구성요소2 { DispatcherServlet 동작 흐름, 요청 선처리작업, 요청전달, 요청처리, 예외처리, 뷰렌더링, 요청처리종료 }
Ben의 프로그램2023. 7. 27. 22:39
728x90
DispatcherServlet 이란?
DispatcherServlet 을 프론트 컨트롤러라고 합니다. '프런트'라는 영어 어감이 회사의 대표 번호와 비슷한데요. Spring MVC 에서 DispatcherServlet 은 모든 요청을 받아서 넘겨주는 역할을 수행하는데요. 딱 영어의 '프런트' 어감과 일치하는 것을 볼 수 있습니다. 기본적으로 DispatcherServlet 은 클라이언트의 모든 요청을 받은 다음 이를 처리할 핸들러에게 넘기고 핸들러가 처리한 결과를 받아 사용자에게 응답 결과를 보여준다고 이해하면 됩니다. DispatcherServlet(프런트 컨트롤러)는 여러 컴포넌트를 이용해 작업을 처리합니다.
DispatcherServlet 내부 동작흐름
DispatcherServlet 의 내부 동작흐름을 한번 살펴볼까 합니다. 내부 동작흐름을 보면 내부 코드가 어떻게 구성되어있을지 대충 예감이 가는데요. (사실은 여러분이 Spring MVC 를 간단하게만 사용할 때는 이 부분들을 모두 다 알아야 할 필요는 없습니다. 앞서서 보셨던 Spring MVC 기본 동작 흐름만 이해를 해도 지금 사용하는데 크게 문제는 없습니다. 그러니까 앞으로 이어지는 부분이 무슨 말인지 모르겠다 하더라도 그냥 지나가셔도 된다는 말입니다.) DispatcherServlet 의 내부 동작흐름 이라는 것은 DispatcherServlet 이 내부적으로 이런 코드들을 가지고 있구나를 이해하고자 하는 것입니다. 요청이 들어오면 '요청 선처리 작업'이 우선 처리됩니다. 선처리 작업이 끝나면 'HandlerExecutionChain'을 결정합니다. 그리고 결정이 되면 HandlerExecutionChain 을 실행합니다. 이 실행하는 과정에서 예외가 발생을 했다면 예외처리를 하게 되구요. 예외가 발생하지 않았다면 바로 View 를 렌더링합니다. 렌더링이 끝나면 요청 처리가 종료되고 요청이 처리됩니다.
DispatcherServlet 요청 선처리 작업 : '지역화 Locale'
DispatcherServlet 은 위와 같은 요청 선처리 작업을 수행합니다. Spring MVC 는 '지역화'라는 것을 지원합니다. '지역화'라는 것은 같은 사이트에 접속을 했더라도 어떤 사용자에게는 한글로 된 페이지가, 어떤 사용자에게는 영어로 된 페이지가 노출되는 것을 처리할 수 있는 기능인데요. 이 처리는 각 사용자 웹 브라우저의 언어 세팅을 파악해서 받아오는데요. 브라우저가 보내오는 Header 정보에서 언어 설정 정보를 찾아서 Locale 을 결정을 하게 됩니다. 그리고 나면 이 정보를 가지고 Locale 에 설정한 값에 따라서 각각 다른 언어로된 화면을 보여주게 하는 것을 '지역화'라고 합니다. 이런 부분들을 선처리 하는 것이죠.
DispatcherServlet 요청 선처리 작업 : RequestContextHolder
RequestContextHolder 에 요청을 저장한다라는 것이 이어지는데요. 이것은 Thread Local 객체입니다. 요청을 받아서 응답할 때까지 HttpServletRequest 와 HttpServletResponse 등을 Spring 이 관리하는 객체 안에서 사용할 수 있게 해주는 것을 의미합니다. Spring MVC 를 사용하다보면 Controller 가 가진 Method 안에서 Request 객체가 필요하면 인자에다가 HttpServeltRequest.request 이렇게 선언만 하면 그냥 그 메서드 내에서 Request 객체를 편하게 사용할 수 있게되는데요. 그런 기능을 제공하는 것이 RequestContextHolder 입니다. 이런 방식은 Spring 이 Web 기술에 종속이 되는 그런 문제점을 갖게 합니다. 아주 권장하는 방법은 아님을 알아두시면 좋겠습니다.
DispatcherServlet 요청 선처리 작업 : FlashMap 복원
FlashMap 복원이 이어지는데요. Map 이라는 것은 Spring 3 에서 추가된 것인데요. Map 은 Redirect 로 값을 전달할 때 사용되는 것인데요. 이전에 Redirect 로 값을 전달할 때는 ? Parameter 를 이용했었는데요. 이렇게 하다보면 URL이 굉장히 길어지는 문제가 발생하면서 URL 길이 제한 이슈가 발생합니다. 이런 부분들 때문에 FlashMap 을 지원하는데요. 이런 FlashMap 을 사용하면 Redirect 될 때 딱 한 번 값을 유지시킬 수 있게 해줍니다. 즉, FlashMap 을 복원한다는 것은 현재 Redirect 가 실행되었을 때만 FlashMap 이 복원됩니다.
DispatcherServlet 요청 선처리 작업 : 파일업로드(멀티파트요청)
그 다음 선처리 대상으로는 '파일 업로드'가 요청되었을 때입니다. 파일 업로드를 하기 위해서는 특별한 객체가 필요합니다. 이것에 대해서는 마지막 파트에서 배우게 되는데요. 이때 사용하는 Request 는 HttpServletRequest 가 아니라 다른 객체를 사용하게 됩니다. 이 때 '멀티파트요청'이 들어오게 되면 Request 를 MultipartResolver 가 멀티파트를 결정하게 됩니다. 그리고 나서 실제 요청을 처리하는 핸들러를 결정하고 실행을 하게 됩니다. 여기까지가 DispatcherServlet 이 요청 선처리 과정에서 하는 작업입니다.
요청 선처리 작업시 사용되는 컴포넌트
위에서 살펴본 요청 선처리 작업시 사용되는 컴포넌트에는 다음과 같은 것들이 있습니다.
-) org.springframework.web.servlet.LocaleResolver : 지역 정보를 결정해주는 전략 오브젝트이다. 디폴트인 AcceptHeaderLocalResolver 는 HTTP 헤더의 정보를 보고 지역정보를 설정해준다.
-) org.springframework.web.servlet.FlashMapManager : FlashMap 객체를 조회(retrieve) & 저장을 위한 인터페이스 : RedirectAttributes의 addFalshAttribute 메소드를 이용해서 저장한다. : 리다이렉트 후 조회를 하면 바로 정보는 삭제된다.
-) org.springframework.web.context.request.RequestContextHolder : 일반 빈에서 HttpServletRequest, HttpServletResponse, HttpSession 등을 사용할 수 있도록 한다. : 해당 객체를 일반 빈에서 사용하게 되면, Web에 종속적이게 될 수 있다.
-) org.springframework.web.multipart.MultipartResolver : 멀티파트 파일 업로드를 처리하는 전략
DispatcherServlet 요청 전달 : HandlerMapping & HandlerExecutionChain & HandlerAdapter
요청 전달을 살펴보겠습니다. 요청이 들어오면 HandlerMapping 으로 HandlerExecutionChain 이 결정이 됩니다. HandlerExecutionChain 이 발견이 된 여부에 따라서 처리가 달라집니다. 발견을 못했으면 페이지가 없는 것을 의미하고 이때는 클라이언트에게 Http 404 를 전달하고 요청이 처리됩니다. HandlerExecutionChain 이 발견이 되었다면 실제로 실행을 시키는 HandlerAdapter 가 결정이 됩니다. 그 다음에는 다시 HandlerAdapter 가 발견이 되는 여부에 따라서 실행이 바뀌게 되는데요. HandlerAdapter 가 발견이 안되었다면 이때는 Server의 잘못이므로 ServletException 이 발생을 하게되구요. 요청이 처리됩니다. HandlerAdapter 가 발견이 되었다면 요청이 잘 처리됩니다.
DispatcherServlet 요청 전달시 사용되는 컴포넌트
-) org.springframework.web.servlet.HandlerMapping : HandlerMapping 구현체는 어떤 핸들러가 요청을 처리할지에 대한 정보를 알고 있다. : 디폴트로 설정되어 있는 핸들러매핑은 BeanNameHandlerMapping 과 DefaultAnnotationHandlerMapping 2가지가 설정되어 있다.
-) org.springframework.web.servlet.HandlerExecutionChain : HandlerExecution 구현체는 실제로 호출된 핸들러에 대한 참조를 가지고 있다. 즉, 무엇이 실행되어야 될지 알고 있는 객체라고 말할 수 있으며, 핸들러 실행전과 실행후에 수행될 HandlerInterceptor 도 참조하고 있다.
-) org.springframework.web.servlet.HandlerAdapter : 실제 핸들러를 실행하는 역할을 담당한다. : 핸들러 어댑터는 선택된 핸들러를 실행하는 방법과 응답을 ModelAndView로 변화하는 방법에 대해 알고 있다. : 디폴트로 설정되어 있는 핸들러어댑터는 HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, AnnoationMethodHandlerAdapter 3가지이다. @RequestMapping 과 @Controller 애노테이션을 통해 정의되는 컨트롤러의 경우 DefaultAnnotationHandlerMapping 에 의해 핸들러가 결정되고, 그에 대응되는 AnnotationMethodHandlerAdapter 에 의해 호출이 일어난다.
DispatcherServlet 요청 처리
요청 처리에 대해서 살펴보도록 하겠습니다. HandlerExecutionChain 이 결정이되었다면 가능한 인터셉터가 존재하는지 파악하는데요.(인터셉터는 파트5에서 배우는데요. 일종의 필터입니다. 처리하기 전에 거쳐서 지나가게 하는 것입니다.) 사용가능한 인터셉터가 존재한다면 인터셉터의 preHandle 를 호출해서 요청을 처리합니다. 인터셉터가 존재하지 않는다면 핸들러가 실행된 이후에 리턴하는 결과가 ModelAndView 객체이고 ModelAndView 객체가 뷰를 갖고 있지 않다면 RequestToViewNameTranslator 가 동작을 하게 되구요. 그 이외에는 인터셉터의 postHandle를 호출해서 요청을 처리합니다. 마지막으로 뷰렌더링이 진행되어 결과를 보여주게 됩니다.
DispatcherServlet 요청 처리시 사용된 컴포넌트
-) org.springframework.web.servlet.ModelAndView : ModelAndView 는 Controller 의 처리 결과를 보여줄 view 와 view에서 사용할 값을 전달하는 클래스이다.
-) org.springframework.web.servlet.RequestToViewNameTranslator : 컨트롤러에서 뷰 이름이나 뷰 오브젝트를 제공해주지 않았을 경우 URL과 같은 요청정보를 참고해서 자동으로 뷰 이름을 생성해주는 전략 오브젝트이다. 디폴트는 DefaultRequestToViewNameTranslator 입니다.
DispatcherServlet 예외처리
예외가 발생하면 HandlerExceptionResolver 에게 문의를 한 다음에 ModelAndView 가 있다면 요청처리를 재개하고 없다면 예외를 다시 던지게 됩니다.
DispatcherServlet 예외 처리시 사용된 컴포넌트
-) org.springframework.web.servlet.HandlerExceptionResolver : 기본적으로 DispatcherServlet이 DefaultHandlerExceptionResolver를 등록한다. : HandlerExceptionResolver는 예외가 던져졌을 때 어떤 핸들러를 실행할 것인지에 대한 정보를 제공한다.
DispatcherServlet 뷰 렌더링
뷰 렌더링 요청이 왔을 때 뷰가 String 이고 ViewResolver로 View 구현체를 찾는데, View 구현체가 없다면 ServletException 을 던지게 됩니다. View 구현체를 찾았다면 View 구현체로 렌더링을 수행하고 요청 처리 재개를 합니다.
DispatcherServlet 뷰 렌더링시 사용된 컴포넌트
-) org.springframework.web.servlet.ViewResolver : 컨트롤러가 리턴한 뷰 이름을 참고해서 적절한 뷰 오브젝트를 찾아주는 로직을 가진 전략 오프젝트이다. : 뷰의 종류에 따라 적절한 뷰 리졸버를 추가로 설정해줄 수 있다.
DispatcherServlet 요청 처리 종료
HandlerExecutionChain 이 존재한다면 인터셉터의 afterCompletion 메소드를 실행하게 됩니다. 그 이후에 RequestHandledEvent 가 발생됩니다. HandlerExecutionChain 이 존재하지 않았다면 바로 RequestHandlerEvent가 발생됩니다. 그리고 최종적으로 요청이 처리됩니다. Spring 을 더 공부하다보면 특정 이벤트가 발생했을 때 별도의 동작을 할 수 있도록 처리할 수 있다는 것 정도만 알아두시면 좋을 것 같습니다.