[6] 스프링 MVC (6) - 스프링 MVC 구조 (DispatcherServlet / HandlerMapping / HandlerAdapter / viewResolver)

김정욱·2021년 5월 12일
0

[6] 스프링 MVC

목록 보기
6/13
post-thumbnail

직접 만든 MVC 프레임 워크와 비교

  • 앞선 2개의 포스팅을 통해 프론트 컨트롤러(Front Controller) 패턴MVC 프레임워크를 직접 만듬
  • 직접 만든 MVC 프레임워크와 우리가 사용할 스프링 MVC내부 구조동일하다
  • 직접 만든 프레임워크 --> 스프링 MVC
    • FrontController --> DispatcherServlet
    • handlerMappingMap --> HandlerMapping
    • MyHandlerAdapyer --> HandlerAdapter
    • ModelView --> ModelAndView
    • viewResolver --> viewResolver
    • MyView --> View

스프링 MVC 구조

[ 설명 ]

  • 스프링 MVC도 앞서 직접 만든 것동일하게 프론트 컨트롤러(Front Controller) 패턴으로 구현
  • 프론트 컨트롤러(Front Controller) 역할을 하는 것이 바로 디스패처 서블릿(Dispatcher Servler)
  • 디스패처 서블릿스프링 MVC핵심

[ DispatcherServlet ]

( 설명 )

  • 부모 클래스를 따라 가면 결국 HttpServlet을 상속 받아서 사용하는 서블릿임을 알 수 있다
    • DispatcherServler --> FrameworkServler --> HttpServletBean --> HttpServlet
  • 스프링 부트DispatcherServlet서블릿으로 자동 등록하면서 모든 경로에 대해 매핑
    (이렇게 모든경로에 대해 매핑된 다음 우리가 지정하는 url경로에 대해 매핑된다)
  • 서블릿호출되면 HttpServletservice()호출
    --> FrameworkServlet에서 service()오버라이드 해두었기 때문에 이를 통해서 서블릿이 시작된다
    --> 이를 통해서 DispatcherServlet.doDispatch()호출되며 우리의 비즈니스 로직시작
           (결국 스프링 MVC의 핵심 동작DispatcherServlet.doDispatch()모두 기술)

( DispatcherServlet.doDispatch() )

  • DispatcherServlet.doDisaptch() 핵심 동작 코드
    (예외처리 / 인터셉터 기능제외간단한 로직만 정리)
protected void doDispatch(HttpServletRequest request, HttpServletResponseresponse) throws Exception {
  HttpServletRequest processedRequest = request;
  HandlerExecutionChain mappedHandler = null;
  ModelAndView mv = null;

  // 1. 핸들러 조회
  mappedHandler = getHandler(processedRequest);
  if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
  }

  // 2. 핸들러 어댑터 조회 - 핸들러를 처리할 수 있는 어댑터
  HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

  // 3. 핸들러 어댑터 실행 -> 4. 핸들러 어댑터를 통해 핸들러 실행 -> 5. ModelAndView 반환
  mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  /* 아래 정의된 함수를 호출 */
  processDispatchResult(processedRequest, response, mappedHandler, mv,dispatchException);
}

/* =================호출함수들=================== */
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
  // 뷰 렌더링 호출
  render(mv, request, response);
}

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
  View view;
  String viewName = mv.getViewName();

  // 6. 뷰 리졸버를 통해서 뷰 찾기, 7. View 반환
  view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
  // 8. 뷰 렌더링
  view.render(mv.getModelInternal(), request, response);
}
  • 동작 순서
    • 핸들러 조회 : 핸들러 매핑을 통해 요청 URL매핑된 핸들러(컨트롤러)를 조회
    • 핸들러 어댑터 조회 : 핸들러실행할 수 있는 핸들러 어댑터 조회
    • 핸들러 어댑터 실행 : 핸들러 어댑터를 통해 실제 핸들러호출해서 실행
    • ModelAndView 반환 : 핸들러 어댑터핸들러가 반환하는 정보ModelAndView로 변환해서 반환
    • viewResolver 호출 & 실행 : 뷰 리졸버를 찾고 실행해서 view 반환
    • 뷰 렌더링

( 인터페이스 )

  • 인터페이스 살펴보기
    • 지금까지 설명한 대부분을 확장 가능한 인터페이스로 제공한다
    • 스프링 MVC의 강점DispatcherServlet 코드 변경 없이 원하는 기능변경하거나 확장 가능한 것
    • 인터페이스만 구현해서 DispatcherServlet등록하면 나만의 컨트롤러를 만들 수도 있다
      (만들일은 거의 없음)
  • 주요 인터페이스
    • 핸들러 매핑 : org.springframework.web.servlet.HandlerMapping
    • 핸들러 어댑터 : org.springframework.web.servlet.HandlerAdapter
    • 뷰 리졸버 : org.springframerwork.web.servlet.viewResolver
    • 뷰 : org.springframerwork.web.servlet.View

( 정리 )

  • 스프링 MVC복잡해서 내부 핵심 구조만 잘 알고 있어도 충분
    --> 직접 기능을 확장하거나 나만의 컨트롤러를 만들 일은 거의 없기 때문
    --> 이미 수 많은 기능이 개발되어있기 때문에 필요한 거의 모든 것이 이미 존재
  • 스프링 MVC의 핵심 구조를 알아야 향후 문제가 발생했을 때 어떤 부분에서 발생했는지 파악이 가능

[ 핸들러 매핑 / 핸들러 어댑터 / 뷰 리졸버 ]

( 개요 )

  • 스프링 MVC의 구조에서 핵심적인 역할을 하는 것은 크게 3가지
    • 핸들러 매핑 : url에 해당하는 핸들러를 조회
    • 핸들러 어댑터 : 핸들러를 처리할 수 있는 핸들러 어댑터 (실제 핸들러 호출)
    • 뷰 리졸버 : 논리이름파일의 실제 물리주소변환
  • 이 중 핸들러 매핑 / 핸들러 어댑터스프링 MVC에서 어떻게 구체적으로 동작하는지 이해해보자
  • 지금은 사용하지 않는 인터페이스를 통해 내부 동작 흐름쉽게 이해할 수 있다

( 핸들러 매핑 / 핸들러 어댑터 동작 이해 ) - Controller 인터페이스를 활용

  • 지금은 사용하지 않는 Controller라는 인터페이스상속OldController가 호출되는 과정을 알아보자
  • 우선 해당 핸들러(컨트롤러)호출되려면 2가지가 필요
    • HandlerMapping(핸들러 매핑)
      : 해당 url 경로를 처리할 수 있는 핸들러 매핑을 찾고 해당하는 핸들러 반환
      ex) 스프링 빈의 이름으로 핸들러를 찾을 수 있는 핸들러 매핑이 필요
    • HandlerAdapter(핸들러 어댑터)
      : 핸들러 매핑을 통해서 찾은 핸들러실행할 수 있는 핸들러 어댑터가 필요
      ex) Controller 인터페이스를 실행할 수 있는 핸들러 어댑터를 찾고 실행
  • HandlerMapping / HandlerAdapter 처리 우선순위
/* HandlerMapping */
0 = RequestMappingHandlerMapping // @RequestMapping의 핸들러 매핑
1 = BeanNameUrlHandlerMapping // 스프링 빈(Bean)의 이름으로 핸들러를 찾음
...
/* HandlerAdapter */
0 = RequestMappingHandlerAdapter // @RequestMapping의 핸들러 어댑터
1 = HttpRequestsHandlerAdapter // HttpRequestHandler 처리
2 = SimpleControllerHandlerAdapter // Controller 인터페이스 처리
...
  • 동작 순서
    • 핸들러 매핑 찾기 & 핸들러 반환
      • HandlerMapping순서대로 실행해서 실행할 수 있는 핸들러 매핑을 찾아야 함
      • 이 경우 빈(Bean) 이름으로 핸들러를 찾아야 하기 때문BeanNameUrlHandlerMapping을 찾음
        (HandlerMapping 처리 우선순위에서 1 에 해당)
      • 찾은 핸들러인 OldController반환
    • 핸들러 어댑터 조회
      • HnadlerAdaptersupports()순서대로 호출하며 처리할 수 있는 핸들러 어댑터를 찾음
      • 이 경우에 SimpleControllerHandlerAdapterController 인터페이스를 지원하므로 대상이 됨
        (HandlerAdapter 처리 우선순위에서 2 에 해당)
    • 핸들러 어댑터 실행
      • 위에서 찾은 핸들러 어댑터SimpleSontrollerHandlerAdapter를 통해 실제 OldController 실행
      • ModelAndView 객체를 반환

( 뷰 리졸버 - viewResolver )

  • 논리 뷰 이름을 통해서 실제 물리 주소매핑해주는 역할
  • 스프링 부트자동 등록하는 뷰 리졸버
0 = BeanNameViewResolver // 빈 이름으로 뷰를 찾아서 반환
1 = InternalResourceViewResolver // JSP를 처리할 수 있는 뷰를 반환
  • 스프링 부트JSP 사용을 위해 InternalResourceViewResolver라는 뷰 리졸버자동으로 등록
  • application.propertiesprefix / suffix설정 정보를 사용해서 등록해야 함
    • spring.mvc.view.prefix : /WEB-INF/views
    • spring.mvc.view.suffix : .jsp

[ @RequestMapping ]

( 설명 )

  • @RequestMapping역시 핸들러 매핑 / 핸들러 어댑터를 통해 실제 핸들러(컨트롤러)가 호출되는 같은 원리
  • RequestMapping인터페이스
    • RequestMappingHandlerMapping : RequestMapping의 핸들러 매핑
    • RequestMappingHandlerAdapter : RequestMapping의 핸들러 어댑터

( 동작 순서 )

  • HTTP Request를 통해 요청이 들어온다
  • 핸들러 매핑 찾기
    • HandlerMapping순회하며 해당하는 RequestMappingHandlerMapping 반환
  • 핸들러 반환
    • RequestMappingHandlerMapping스프링 빈(Bean) 중에 @RequestMapping 또는 @Controller클래스 레벨에 있는 것을 찾아서 매핑 정보로 인식
    • 매핑정보url에 해당되는 핸들러반환
  • 핸들러 어댑터 찾기
    • HandlerAdaptersupports()순서대로 호출하며 처리할 수 있는 핸들러 어댑터를 찾음
      (이 경우 RequestMappingHandlerAdapter 반환)
  • 핸들러 어댑터 실행
    • RequstMappingHandlerAdapter실제 핸들러 호출
    • ModelAndView반환
      (실무에서는 viewName만 반환해서 사용함 --> 사용성이 더 좋기 때문)
  • viewResolver
    • 논리 주소실제 물리 주소매핑
  • view
    • ModelAndView에 있는 데이터를 통해 view 렌더링

( 추가 )

  • REST API만 개발하는 경우에는 조금 동작이 다르다
    • view가 없으니 viewResolver도 동작하지 X
    • 템플릿 엔진을 사용한 개발 시에만 viewResolver 동작 O

스프링 MVC 시작

  • 클래스 레벨@RequestMapping으로 공통 path를 지정
  • @RequestMapping편리하게 사용하기 위한 @GetMapping() / @PostMapping을 사용
  • @RequestParam을 이용해서 요청 변수바로 받기
  • ModelAndView 객체직접 반환하는 것이 아니라 viewName만으로 처리해서 사용성이 좋아짐
profile
Developer & PhotoGrapher

0개의 댓글