티스토리 뷰

2) Spring MVC구성요소

Spring MVC 기본 동작 흐름

  • Database라고 되어있는 부분을 제외한 파란색 부분들은 모두 Spring MVC가 제공해주는 것들입니다. 개발자가 만들어야 하는 부분들은 보라색으로 되어있는 부분들이고 녹색으로 되어있는 View Spring이 제공하는 부분도 있고 개발자가 만들어야 되는 부분도 같이 존재한다고 생각하면 됩니다.
  • 그림을 보면 제일 먼저 클라이언트가 1번처럼 요청을 보냅니다. 보낸 모든 요청은 Dispatcher Servlet이라고 하는 서블릿 클래스가 받습니다.
  • Dispatcher Servlet은 요청을 처리해줄 컨트롤러와 메서드가 무엇인지 Handler Mapping에게 물어봅니다. Hanlder Mapping은 생성될 때 어떤 요청에 어떤 컨트롤러가 동작할지를 어노테이션으로 설정한 xml 파일이나 java 파일로부터 얻어와서 관리하는 객체입니다. 그리고 Dispatcher ServletHandler Mapping으로부터 지금 들어온 요청에 알맞은 컨트롤러가 무엇인지 해당되는 메서드는 무엇인지에 대한 정보를 알아내게 됩니다.
  • 알아낸 다음에는 알맞은 Handler Adapter에게 실행을 요청합니다. 그리고 그때 결정된 컨트롤러와 해당 메서드가 실행한 뒤 그 결과를 Model에 받아서 Dispatcher Servlet에게 전달을 하게 됩니다.
  • 이때 Dispatcher Servlet은 컨트롤러가 리턴한 view name을 알아오게 되는데 해당 view name을 가지고 적절한 View Resolver를 통해서 뷰를 출력하게 됩니다. 그러니까 View Resolver가 해당 View의 정보를 정확하게 알려주게 되면 이 View로 응답을 하게 됩니다.
  • Spring MVC는 이 그림의 순서들만 정확하게 이해하고 계시면 크게 어렵지 않게 여러분들이 하실 수 있거든요. 한 번에 이해가 되지 않으셨다면 여러 번 보면서 머릿속에 집어넣으면 좋겠습니다.

요청 처리를 위해 사용되는 컴포넌트

  • DispatcherServlet
  • HandlerMapping
  • HandlerAdapter
  • MultipartResolver
  • LocaleResolver
  • ThemeResolver
  • HandlerExceptionResolver
  • RequestToViewNameTranslator
  • ViewResolver
  • FlashMapManager
  • Spring MVC를 이해한다는 것은 DispatcherServlet이 어떻게 동작하는지를 이해하는 것이라고 말할 수 있습니다. DispatcherServlet이 요청을 받아서 결과를 출력하기까지 여러 컴포넌트를 사용하게 될텐데 DispatcherServlet DispatcherServlet에서 사용하는 각각의 컴포넌트들을 지금부터 조금 더 자세히 알아보도록 하겠습니다.

DispatcherServlet

  • 프론트 컨트롤러 (Front Controller)라고도 합니다프론트 컨트롤러란 어떤 회사 같은 곳의 대표 번호로 전화를 걸었을 때 총무부, 기타 부서 등으로 전화를 넘기는 역할과 비슷하다고 보시면 됩니다. Spring MVC에서 DispatcherServlet이 그런 일을 합니다.
  • 클라이언트의 모든 요청을 받은 후 이를 처리할 핸들러에게 넘기고 핸들러가 처리한 결과를 받아 사용자에게 응답 결과를 보여줍니다. 모든 요청을 다 받아서 그 요청을 처리하지 않고 넘겨주는 일까지만 합니다.
  • DispathcerServlet은 여러 컴포넌트를 이용해 작업을 처리합니다.
  • 프론트 컨트롤러는 이론적으로는 한 개 이상 사용될 수 있다고 하는데 보통은 한 개만 선언해서 사용을 합니다. 그리고 이런 DispatcherServlet은 여러 컴포넌트들을 이용해서 작업을 처리합니다.

Spring MVC - DispatcherServlet 내부 동작흐름

DispatcherServlet 내부 동작흐름

DispatcherServlet 내부 동작흐름

  • DispatcherServlet은 일단 요청이 들어오면 요청에 대한 선처리 작업을 합니다. 선처리 작업이 끝나면 HandlerExecutionChain을 결정하고 그리고 결정되면 HandlerExecutionChain을 실행해요. 그런데 실행을 하는 과정에서 혹시 예외가 발생하면 예외 처리를 하게 될 거고 예외가 발생하지 않았다면 바로 뷰를 렌더링 할 겁니다. 그리고 요청 처리를 종료하면 끝나는 겁니다. 응답하고 끝나겠죠. 크게 이런 방식으로 처리가 됩니다.

DispatcherServlet  내부 동작흐름 상세  -  요청 선처리 작업

DispatcherServlet  내부 동작흐름 상세  -  요청 선처리 작업

  • DispatcherServlet가 요청을 선처리하는 작업에서는 아래와 같은 일들을 합니다.
  • Spring MVC는 지역화(locale 처리)라는 것을 지원합니다. 지역화라는 게 뭐냐면 똑같은 사이트라도 어떤 사용자한테는 영어로 된 화면이 보여지고 어떤 사용자한테는 한국어로 된 화면이 보여지는데 그렇게 처리하게 할 수 있다는 소리에요.
  • 그런데 지역화를 어떻게 처리하는 거냐면 브라우저의 언어 세팅 정보들을 브라우저의 헤더에서 받아서 이런 Locale을 결정할 수가 있습니다. 그리고 그 Locale에 설정한 값에 따라 각각 다른 언어로 된 화면을 보여주게 됩니다. 즉, 이런 처리를 지역화라고 합니다.
  • 그 다음에 RequestContextHolder에 요청을 저장합니다. 이 부분은 스레드 로컬 객체에요. 요청을 받아서 응답할 때까지 HttpServletRequest, HttpServletResponse 등을 Spring이 관리하는 객체 안에서 사용할 수 있도록 합니다.
  • Spring MVC를 사용하다가 컨트롤러가 가진 메서드 안에서 Request 객체가 필요할 때 인자에다가 HttpServletRequest request 선언만 하면 그 메서드 내에서 Request를 사용할 수 있습니다. 그런 부분들을 제공한다는 이야기입니다.
  • 그러나 이런 방식은 Spring이 웹 기술에 종속되는 문제점을 가지니까 아주 권장하는 방법은 아닙니다막 가져다 쓰면 안 되고 이에 대한 대책으로 Spring이 그에 해당하는 것들을 제공하니까 그런 것들을 가져다 쓰는 편이 좋습니다.
  • 그다음에 이제 또 무슨 작업을 하냐면 FlashMap을 복원합니다. 이 FlashMap Spring 3에서 추가된 기능인데 redirect로 값을 전달할 때 사용됩니다. redirect로 값 전달할 때 ?, 파라미터 이런 것들 이용합니다. 그런데 이렇게 하다 보면 URL이 굉장히 복잡해집니다. URL 길이에 대한 제한도 있고요FlashMap을 사용하면 redirect 될 때 딱 한 번 값을 유지시킬 수 있게 해줍니다. , FlashMap을 복원한다는 것은 현재 실행이 redirect 되었을 때만 실행이 되는 것들을 위해 제공되는 기능입니다.
  • 마지막으로 사용자가 파일 업로드를 하는 경우에 대한 선처리가 필요합니다. 파일 업로드에는 파일 정보를 읽어들이는 특수한 형태의 Request 객체가 필요합니다. 멀티 파트라는 요청이 들어오게 되면 Request MultipartResolver가 이런 멀티 파트를 결정하게 해줍니다. 그리고 나서 실제 요청을 처리하는 핸들러를 결정하고 실행을 하는 역할까지 해주는 것이 선처리 작업에서 하는 일입니다.

요청 선처리 작업시 사용되는 컴포넌트

  • org.springframework.web.servlet.LocaleResolver
    • 지역 정보를 결정해주는 전략 오브젝트이다.
    • 디폴트인 AcceptHeaderLocalResolver HTTP 헤더의 정보를 보고 지역정보를 설정해준다.
  • org.springframework.web.servlet.FlashMapManager
    • FlashMap객체를 조회(retrieve) & 저장을 위한 인터페이스
    • RedirectAttributes addFlashAttribute메소드를 이용해서 저장한다.
    • 리다이렉트 후 조회를 하면 바로 정보는 삭제된다.
  • org.springframework.web.context.request.RequestContextHolder
    • 일반 빈에서 HttpServletRequest, HttpServletResponse, HttpSession 등을 사용할 수 있도록 한다.
    • 해당 객체를 일반 빈에서 사용하게 되면, Web에 종속적이 될 수 있다.
  • org.springframework.web.multipart.MultipartResolver
    • 멀티파트 파일 업로드를 처리하는 전략

DispatcherServlet 내부 동작흐름 상세 - 요청 전달

DispatcherServlet 내부 동작흐름 상세 - 요청 전달

  • HandlerMapping으로 HandlerExecutionChain이 결정되고 이 때 HandlerExecutionChain을 발견했는지, 발견하지 않았는지에 따라서 어떻게 처리할 것인지 달라집니다.
  • 만약 발견 못 했으면 페이지가 없는 거니까 이때는 Http 404를 전달합니다.
  • 페이지가 존재하면 HandlerAdapter가 결정됩니다. 만약 결정된 HandlerAdapter가 없다면 서버상의 오류이므로 ServletException이 발생이 됩니다.
  • 문제없이 처리가 됐다면 요청이 잘 처리됩니다.

요청 전달시 사용되는 컴포넌트

  • org.springframework.web.servlet.HandlerMapping
    • HandlerMapping구현체는 어떤 핸들러가 요청을 처리할지에 대한 정보를 알고 있다.
    • 디폴트로 설정되는 있는 핸들러매핑은 BeanNameHandlerMapping DefaultAnnotationHandlerMapping 2가지가 설정되어 있다.
  • org.springframework.web.servlet.HandlerExecutionChain
    • HandlerExecutionChain구현체는 실제로 호출된 핸들러에 대한 참조를 가지고 있다.
    • , 무엇이 실행되어야 될지 알고 있는 객체라고 말할 수 있으며, 핸들러 실행전과 실행후에 수행될 HandlerInterceptor도 참조하고 있다.
  • org.springframework.web.servlet.HandlerAdapter
    • 실제 핸들러를 실행하는 역할을 담당한다.
    • 핸들러 어댑터는 선택된 핸들러를 실행하는 방법과 응답을 ModelAndView로 변화하는 방법에 대해 알고 있다.
    • 디폴트로 설정되어 있는 핸들러어댑터는 HttpRequestHandlerAdapter, SimpleControllerHandlerAdapter, AnnotationMethodHanlderAdapter 3가지이다.
    • @RequestMapping @Controller 애노테이션을 통해 정의되는 컨트롤러의 경우 DefaultAnnotationHandlerMapping에 의해 핸들러가 결정되고, 그에 대응되는 AnnotationMethodHandlerAdapter에 의해 호출이 일어난다.

DispatcherServlet 내부 동작흐름 상세 - 요청 처리

DispatcherServlet 내부 동작흐름 상세 - 요청 처리

  • HandlerExecutionChain이 결정되었다면 사용 가능한 인터셉터가 있는지 찾습니다.
  • 인터셉터에 대해서 간단하게 설명하자면 일종의 필터라고 생각하면 됩니다. 그러니까 뭔가 처리하기 전에 한번 거쳐서 지나가게 하는 것이라고 생각하면 됩니다.
  • 사용 가능한 인터셉터가 존재한다면 인터셉터의 preHandle을 호출해서 요청을 처리하고 계속 요청을 처리하는 경우 핸들러가 실행됩니다.
  • 핸들러가 실행된 후에 어떤 값을 리턴하게 되는데 리턴하는 결과가 ModelAndView 객체이고 또 ModelAndView 객체가 뷰를 갖고 있지 않다면 RequestToViewNameTranslator 가 동작하게 됩니다. 그 이외에는 인터셉터의 postHandle을 호출해서 요청을 처리하고 뷰 렌더링을 해서 결과를 보여주게 됩니다.

요청 처리시 사용된 컴포넌트

  • org.springframework.web.servlet.ModelAndView
    • ModelAndView Controller의 처리 결과를 보여줄 view view에서 사용할 값을 전달하는 클래스이다.
    • 전에 우리가 서블릿에서 모델을 얻고 그 모델을 JSP로 넘길 때 Request 같은 객체를 이용했었는데 Spring에서는 종속되지 않게 하기 위해 ModelAndView 같은 객체를 제공하고 있습니다. Request에다가 값을 넣어서 사용하는 것보다 이런 컴포넌트를 이용하는 것이 훨씬 바람직합니다.
  • org.springframework.web.servlet.RequestToViewNameTranslator
    • 컨트롤러에서 뷰 이름이나 뷰 오브젝트를 제공해주지 않았을 경우 URL과 같은 요청정보를 참고해서 자동으로 뷰 이름을 생성해주는 전략 오브젝트이다. 디폴트는 DefaultRequestToViewNameTranslator이다.
    • 이 부분은 적절한 뷰가 없을 때 작동합니다.

DispatcherServlet 내부 동작흐름 상세 - 예외처리

DispatcherServlet 내부 동작흐름 상세 - 예외처리

  • 예외가 발생하면 HandlerExceptionResolver에 문의를 한 후에 ModelAndView가 있다면 요청 처리를 재개하고 그렇지 않으면 다시 예외를 던지게 되도록 처리합니다.

예외 처리시 사용된 컴포넌트

  • org.springframework.web.servlet.HandlerExceptionResolver
    • 기본적으로 DispatcherServlet DefaultHandlerExceptionResolver를 등록한다.
    • HandlerExceptionResolver는 예외가 던져졌을 때 어떤 핸들러를 실행할 것인지에 대한 정보를 제공한다.

DispatcherServlet 내부 동작흐름 상세 - 뷰 렌더링 과정

DispatcherServlet 내부 동작흐름 상세 - 뷰 렌더링 과정

  • 뷰 렌더링 요청이 왔을 때 뷰가 String이라면 ViewResolver로 구현체를 찾고 ViewResolver의 구현체를 찾지 못했다면 ServletException을 발생시키게 됩니다. 뷰의 구현체를 찾았다면 렌더링을 하고 요청 처리를 계속 진행합니다.

뷰 렌더링 과정시 사용된 컴포넌트

  • org.springframework.web.servlet.ViewResolver
    • 컨트롤러가 리턴한 뷰 이름을 참고해서 적절한 뷰 오브젝트를 찾아주는 로직을 가진 전략 오프젝트이다.
    • 뷰의 종류에 따라 적절한 뷰 리졸버를 추가로 설정해줄 수 있다.

DispatcherServlet 내부 동작흐름 상세 - 요청 처리 종료

DispatcherServlet 내부 동작흐름 상세 - 요청 처리 종료

  • 요청 처리 마지막에는 HandlerExecutionChain이 존재한다면 인터셉터의 afterCompletion 메서드를 실행하게 됩니다. 그리고 나서 RequsetHandledEvent를 발생시킵니다. HandlerExecutionChain이 존재하지 않는다면 바로 RequestHandledEvent가 발생됩니다. 그러면서 요청이 처리됩니다.

생각해보기

  • DispacherServlet은 요청을 받은 후, 요청을 처리하기 위해서 여러가지 작업을 수행하고 있습니다
  • 개발자는 DispacherServlet이 어떤 방식으로 동작하는지 이해한다면 좀 더 잘 Spring MVC를 잘 사용할 수 있습니다.
  • Spring외에 다른 프레임워크를 학습할 때에도, 해당 프레임워크의 동작원리를 이해하는 것은 굉장히 중요합니다.
  • 어떻게 하면, 다른 사람이 만든 라이브러리나 프레임워크를 좀 더 잘 이해할 수 있을지 고민해보세요.

참고 자료

[참고링크] Overview of Spring MVC Architecture
https://terasolunaorg.github.io/guideline/1.0.1.RELEASE/en/Overview/SpringMVCOverview.html

[참고링크] Modules           https://docs.spring.io/spring/docs/3.0.0.M4/reference/html/ch01s02.html

[참고링크] Spring MVC - DispatcherServlet 동작 원리 출처: http://jess-m.tistory.com/15 [Jess's Home]                          http://jess-m.tistory.com/15

[참고링크] Spring DispatcherServlet의 기본 7가지 DI전략           http://kimddochi.tistory.com/86

[참고링크] Spring MVC – Full java based config
https://samerabdelkafi.wordpress.com/2014/08/03/spring-mvc-full-java-based-config/

[참고링크] web.xml vs Initializer with Spring  http://www.baeldung.com/spring-xml-vs-java-config

[참고링크] Spring – Mixing XML and JavaConfig
https://www.mkyong.com/spring/spring-mixing-xml-and-javaconfig/

[참고링크] arey/spring-javaconfig-sample
https://github.com/arey/spring-javaconfig-sample/blob/master/src/main/webapp/WEB-INF/web.xml

[참고링크] WebMvcConfigurationSupport
https://docs.spring.io/spring/docs/3.0.6.RELEASE_to_3.1.0.BUILD-SNAPSHOT/3.1.0.BUILD-SNAPSHOT/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.html

[참고링크] Spring mvc (2) 그리고 Spring boot     http://wonwoo.ml/index.php/post/1590

[참고링크] Migrate Spring MVC servlet.xml to Java Config
https://www.luckyryan.com/2013/02/07/migrate-spring-mvc-servlet-xml-to-java-config/

[참고링크] Spring RequestMapping         http://www.baeldung.com/spring-requestmapping

 

Comments