-
[스프링 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")
'INFLEARN > 스프링 MVC 1편' 카테고리의 다른 글
[스프링 MVC 1] 7. 스프링 MVC - 웹 페이지 만들기 (0) 2021.11.05 [스프링 MVC 1] 6. 스프링 MVC - 기본 기능 (0) 2021.11.05 [스프링 MVC 1] 4. MVC 프레임워크 만들기 (0) 2021.10.23 [스프링 MVC 1] 3. 서블릿, JSP, MVC 패턴 (0) 2021.10.18 [스프링 MVC 1] 2. 서블릿 (0) 2021.10.16