ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링 MVC 1] 5. 스프링 MVC - 구조 이해
    INFLEARN/스프링 MVC 1편 2021. 10. 27. 00:08

    5. 스프링 MVC - 구조 이해

     

    [강의 정리] 김영한님 :: 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

     

     

    1. 스프링 MVC 전체 구조

     

    # Spring MVC 구조

    • 지금까지 직접 구현해본 프레임워크와 스프링 MVC를 비교하면 거의 유사
    • FrontController -> DispatcherServlet
    • ViewResolver와 View는 인터페이스로 제공됨 

    출처 : 김영한님 강의 자료

     

     

    # DispatcherServlet 구조 살펴보기

    org.springframework.web.servlet.DispatcherServlet
    • 스프링 MVC도 프론트 컨트롤러 패턴으로 구현
    • 즉, 디스패처 서블릿(Dispatcher Servlet)이 스프링 MVC의 프론트 컨트롤러

     

    • DispatcherServlet도 부모 클래스에서 HttpServlet을 상속 받아서 사용하고, 서블릿으로 동작
    • 스프링 부트는 DispatcherServlet을 서블릿으로 자동으로 등록하고 모든 경로(urlPatterns="/")에 대해서 매핑 (자세한 경로가 우선순위가 더 높음)

     

    # doDispatch()

    • 서블릿이 호출되면 HttpServlet이 제공하는 service()가 호출 (스프링 MVC는 FrameworkServlet에서 오버라이드 해둠)
    • FrameworkServlet.service()를 시작으로 여러 메서드가 호출되면서 DispaterServlet.doDispatch()호출

    (1) 핸들러 조회

    • 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러) 조회

    (2) 핸들러 어댑터 조회

    • 핸들러를 실행할 수 있는 핸들러 어댑터 조회

    (3) 핸들러 어댑터 실행

    (4) 핸들러 어댑터를 통해 (실제) 핸들러 실행

    (5) ModelAndView 반환

    • 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환

    (6) 뷰 리졸버 통해서 뷰 찾기

    • ex) JSP : InternalResourceViewResolver가 자동 등록되고 사용됨

    (7) View 반환

    • 뷰 리졸버 : 논리 이름 -> 물리 이름, 렌더링 역할을 담당하는 뷰 객체를 반환
    • ex) JSP : InternalResourceView(JstlView)를 반환
    • 내부에 forward()로직이 있음

    (8) 뷰 렌더링

    • 뷰를 통해서 뷰를 렌더링

     

     

    # 주요 인터페이스

    • Spring MVC의 강점은 DispatcherServlet 코드의 변경 없이 원하는 기능을 변경하거나 확장 가능 (물론 쉬운 일은 아님)
    핸들러 매핑 : org.springframework.web.servlet.HandlerMapping
    핸들러 어댑터 : org.springframework.web.servlet.HandlerAdapter
    뷰 리졸버 : org.springframework.web.servlet.ViewResolver
    뷰 : org.springframework.web.servlet.View

     

     

    2. 핸들러 매핑과 핸들러 어댑터

     

    # Controller 인터페이스

    • 과거 버젼의 스프링 컨트롤러 (
    • +) Controller 인터페이스는 @Controller 애노테이션과 전혀 다름

    # 컨트롤러는 어떻게 호출되는가

    • OldController
    • @Component : /springmvc/old-controller 라는 이름으로 스프링 빈으로 등록 (빈의 이름으로 URL 매핑)
    • 컨트롤러가 호출되려면 2가지가 필요 (핸들러 매핑 / 핸들러 어댑터)

    • HandlerMaaping (핸들러 매핑)
      • 핸들러 매핑에서 해당 컨트롤러를 찾을 수 있어야 함
      • ex) 스프링 빈의 이름으로 핸들러를 찾을 수 있는 핸들러 매핑이 필요
    • HandlerAdapter (핸들러 어댑터)
      • 핸들러 매핑을 통해서 찾은 핸들러를 실행할 수 있는 핸들러 어댑터 필요
      • ex) Controller 인터페이스를 실행할 수 있는 핸들러 어댑터 찾고 실행

     

    # 스프링 부트가 자동 등록하는 핸들러 매핑과 핸들러 어댑터 (OldController)

    • HandlerMapping과 HandlerAdapter를 순서대로 찾음 (실제로는 더 많음..)
    • HandlerMapping 
    0 = RequestMappingHandlerMapping : 애노테이션 기반의 컨트롤러인 @RequestMapping 에서
    1 = BeanNameUrlHandlerMapping : 스프링 빈의 이름으로 핸들러 찾기
    • HandlerAdapter
    0 = RequestMappingHandlerAdapter : 애노테이션 기반의 컨트롤러인 @RequestMapping에서 사용
    1 = HttpRequestHandlerAdapter : HttpRequestHandler 처리
    2 = SimpleControllerHandlerAdapter : Controller 인터페이스

     

    (1) 핸들러 매핑으로 핸들러 조회

    • HandlerMapping을 순서대로 실행해서 핸들러 찾기
    • 빈 이름으로 핸들러 찾아야하므로, BeanNameUrlHandlerMapping 실행에 성공, 핸들러인 OldController 반환

    (2) 핸들러 어댑터 조회

    • HandlerAdapter의 supports()를 순서대로 호출
    • SimpleControllerHandlerAdapter가 Controller 인터페이스를 지원하므로 대상이 됨

     

    (3) 핸들러 어댑터 실행

    • 디스패처 서블릿이 조회한 SimpleControllerHandlerAdapter를 실행하면서 핸들러 정보도 함께 넘겨줌
    • SimpleControllerHandlerAdapter는 핸들러인 OldController를 내부에서 실행하고 결과 반환

     

    # HttpRequestHandler

    • 서블릿과 가장 유사한 형태의 핸들러

    (1) 핸들러 매핑으로 핸들러 조회

    • HandlerMapping을 순서대로 실행해서 핸들러 찾기
    • 빈 이름으로 핸들러 찾아야하므로, BeanNameUrlHandlerMapping 실행에 성공, 핸들러인 MyHttpRequestHandler 반환

    (2) 핸들러 어댑터 조회

    • HandlerAdapter의 supports()를 순서대로 호출
    • HttpRequestHandlerAdapter가 HttpRequestHandler 인터페이스를 지원하므로 대상이 됨

     

    (3) 핸들러 어댑터 실행

    • 디스패처 서블릿이 조회한 HttpRequestHandlerAdapter를 실행하면서 핸들러 정보도 함께 넘겨줌
    • HttpRequestHandlerAdapter는 핸들러인 MyHttpRequestHandler를 내부에서 실행하고 결과 반환

     

    # @RequestMapping

    • 가장 우선순위가 높은 핸들러 매핑 : RequestMappingHandlerMapping
    • 가장 우선순위가 높은 핸들러 어댑터 : RequestMappingHandlerAdapter
    • 실제 개발에서는 거의 @RequestMapping만 사용 
    • 애노테이션 기반의 컨트롤러를 지원

     

     

    3. 뷰 리졸버

     

    # View를 사용할 수 있도록

    • OldController

    • application.properties

     

    # 뷰 리졸버 - InternalResourceViewResolver

    • 스프링 부트는 InternalResourceViewResolver라는 뷰 리졸버를 자동으로 등록
    • 이 때, application.properties에 등록한 설정 정보 사용해서 등록 (prefix, suffix)

     

    # 뷰 리졸버 동작방식 

    • 스프링 부트가 자동 등록하는 뷰 리졸버
    1 = BeanNameViewResolver : 빈 이름으로 뷰를 찾아서 반환
    2 = InternalResourceViewResolver : JSP를 처리할 수 있는 뷰를 반환

    (1) 핸들러 어댑터 호출

    • 핸들러 어댑터를 통해 new-form이라는 논리 뷰 이름 획득

    (2) ViewResolver 호출

    • new-form이라는 뷰 이름으로 viewResolver를 순서대로 호출
    • BeanNameViewResolver는 new-form이라는 이름의 스프링 빈으로 등록된 뷰를 찾지만 없음
    • 그래서 그 다음 순서인 InternalResourceViewResolver가 호출

    (3) InternalResourceViewResolver

    • InternalResourceView 반환

    (4) 뷰 - InternalResourceView

    • JSP처럼 포워드 forward()를 호출해서 처리할 수 있는 경우에 사용

    (5) view.render()

    • 호출되면 InternalResourceView가 forward()를 사용해서 JSP 실행

     

    +) 참고

    • InternalResourceViewResolver는 만약 JSTL라이브러리가 있으면 JstlView를 반환
    • 다른 뷰는 실제 뷰를 렌더링해서 JSP와 달리 forward() 과정 없이 바로 렌더링 됨

     

     

    4. 스프링 MVC - 시작하기

     

    # @RequestMapping

    • RequestMappingHandler
    • RequestMappingAdapter

     

    # SpringMemberFormControllerV1 - 회원 등록 폼

    • @Controller
      • 스프링이 자동으로 스프링 빈으로 등록 (내부에 @Component 애노테이션이 있어서 컴포넌트 스캔의 대상이 됨)
      • 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식
    • @RequestMapping
      • 요청 정보를 매핑
      • 해당 URL이 호출되면 이 메서드가 호출
      • 애노테이션 기반으로 동작하므로 메서드 이름은 임의로 지어도 상관 없음
    • ModelAndView
      • 모델과 뷰 정보 담아서 반환

     

    RequestMappingHandlerMapping은 스프링 빈 중에서 @RequestMapping 또는 @Controller가 클래스 레벨에 붙어 있는 경우에 매핑 정보로 인식 

    (1) @Controller (거의 이 방식만 사용)
    (2) @Component @RequestMapping
    (3) 컴포넌트 스캔 없이 직접 스프링 빈으로 등록해도 가능

     

     

    # SpringMemberSaveControllerV1 - 회원 저장

    • 기존에 만들었던 V3와 유사
    • mv.addObject("member", member) : 스프링이 제공하는 ModelAndView를 통해 Model 데이터를 추가할 때는 addObject() 사용 (이 데이터는 이후 뷰 렌더링 할 때 사용)

     

    #  SpringMemberListControllerV1 - 회원 목록

    • 기존에 만들었던 V3와 유사

     

     

    5. 스프링 MVC -  컨트롤러 통합

     

    # 컨트롤러 통합

    • @RequestMapping은 메서드 단위에 적용되서 앞서 만들었던 회원 등록 폼/회원 저장/회원 목록을 하나로 통합할 수 있음

     

    # 조합

    • 중복 제거 가능 (클래스 레벨에 공통적인 부분 뺌)

     

     

    6. 스프링 MVC - 실용적인 방식

     

    # 매번 ModelView 생성하고 반환하는거 불편

    • 회원 등록 폼

    • 회원 저장

    • 회원 목록

     

    # Model 파라미터

    • save(), members() 를 보면 Model을 파라미터로 받음

     

    # ViewName 직접 반환

    • 뷰의 논리 이름 반환

     

    # @RequestParam 사용

    • 스프링은 HTTP 요청 파라미터를 @RequestParam으로 받을 수 있음
    • GET 쿼리 파라미터, POST Form 방식 모두 지원
    • @RequestParam("username")은 request.getParamter("username")과 거의 같은 코드

     

    # @RequestMapping -> @GetMapping, @PostMapping

    • HTTP Method를 구분 짓지 않으면, GET임에도 불구하고 POST를 날려도 넘어갈 수 있음
    • 그래서 URL만 매칭하는 것이 아니라 HTTP Method도 구분 필요 (아래와 같이)
    @RequestMapping(value = "/new-form", method = RequestMethod.GET)
    • 하지만, 조금 더 편리하게 아래와 같이 사용 가능 (Get, Post, Put, Delete, Patch 모두 애노테이션 있음)
    @GetMapping("/new-form")

    댓글

Programming Diary