SpringMVC 전체 구조
DispatcherServlet
- SpringMVC 의 프런트 컨트롤러
- `HttpServlet` 을 상속받아 사용하고, 서블릿으로 동작
- 스프링 부트가 `DispatcherServlet` 을 서블릿으로 자동 등록하며 모든 경로 (`urlParrerns`) 에 대하여 매핑
SpringMVC 구조
핸들러 매핑 | ||||||
↑ ① 핸들러 조회 ↑ |
핸들러 어댑터 목록 ↗ ② 핸들러 어댑터 조회 ↗ |
|||||
클라이언트 | HTTP 요청 → → → → |
Dispatcher Servlet |
③ handle(handler) → → → → → → ← ← ← ← ← ← ⑤ModelAndView 반환 |
핸들러 어댑터 |
④ 핸들러 호출 → → → → |
핸들러 (컨트롤러) |
↓ ⑧ render(model) 호출 ↓ |
↖ ↘ ⑥ ViewResolver 호출 ↖ ↘ ↖ ⑦ View 반환 ViewResolver |
|||||
HTML 응답 ← ← ← ← |
View |
1. 핸들러 조회 : 핸들러 매핑을 통해 요청 URL 에 매핑된 핸들러를 조회
2. 핸들러 어댑터 조회 : 핸들러를 실행할 수 있는 핸들러 어댑터를 조회
3. 핸들러 어댑터 실행 : 핸들러 어댑터를 실행
4. 핸들러 실행 : 핸들러 어댑터가 실제 핸들러를 실행
5. ModelAndView 반환 : 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView 로 변환하여 반환
6. ViewResolver 호출 : 뷰 리졸버를 찾고 실행
( JSP 의 경우, `InternalResourceViewResolver` 자동 등록 )
7. View 반환 : 뷰 리졸버가 뷰 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 View 객체 반환
( JSP 의 경우, `InternalResourceView(JstlView) 반환, 내부에 `forward()` 로직 )
8. 뷰 렌더링 : 뷰를 통해 뷰를 렌더링
핸들러 매핑 / 핸들러 어댑터
HandlerMapping
0 = RequestMappingHandlerMapping
: 애노테이션 기반의 컨트롤러인 @RequestMapping 에서 사용
1 = BeanNameUrlHandlerMapping
: 스프링 빈 이름으로 핸들러 찾음
( 생략 )
HandlerAdapter
0 = RequestMappingHandlerAdapter
: 애노테이션 기반의 컨트롤러인 @RequestMapping 에서 사용
1 = HttpRequestHandlerAdapter
: HttpRequestHandler 처리
2 = SimpleControllerHandlerAdapter
: Controller 인터페이스 (애노테이션 X, 과거에 사용) 처리
( 생략 )
HandlerAdapter 의 `supports()` 를 순서대로 호출하여 핸들러 어댑터를 조회한다.
Controller 인터페이스 (과거에 사용)
@Component("/springmvc/old-controller")
public class OldController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
return null;
}
}
`/springmvc/old-controller` 이름으로 스프링 빈이 등록되었다.
빈 이름으로 URL 을 매핑한다.
1. HandlerMapping 을 순서대로 실행하여 핸들러를 찾는다.
2. 빈 이름으로 핸들러를 찾는 `BeanNameUrlHandlerMapping` 이 실행에 성공하고 핸들러를 반환한다.
3. HandlerAdapter 의 `supports()` 를 호출하여 핸들러 어댑터를 찾는다.
4. 찾은 `SimpleControllerHandlerAdapter` 로 핸들러를 실행하고, 결과를 반환한다.
HttpRequestHandler 인터페이스 (과거에 사용)
@Component("/springmvc/request-handler")
public class MyHttpRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
`/springmvc/request-handler` 이름으로 스프링 빈이 등록되었다.
빈 이름으로 URL 을 매핑한다.
1. HandlerMapping 을 순서대로 실행하여 핸들러를 찾는다.
2. 빈 이름으로 핸들러를 찾는 `BeanNameUrlHandlerMapping` 이 실행에 성공하고 핸들러를 반환한다.
3. HandlerAdapter 의 `supports()` 를 호출하여 핸들러 어댑터를 찾는다.
4. 찾은 `HttpRequestHandlerAdapter` 로 핸들러를 실행하고, 결과를 반환한다.
@RequestMapping (실무)
실무에서 사용하는 애노테이션 기반의 컨트롤러를 지원하는 매핑과 어댑터인
`RequestMappngHandlerMapping` 과 `RequestMappingHandlerAdapter` 를 가장 많이 사용한다.
- @Controller
: 내부의 @Component 애노테이션으로 인해 컴포넌트 스캔의 대상이 되어 스프링 빈으로 등록
: 스프링 MVC 에서 애노테이션 기반 컨트롤러로 인식
- @RequestMapping
: 요청 정보를 매핑하여 해당 URL 이 호출되면 메서드가 호출된다.
(애노테이션 기반이기 때문에 메서드 이름 임의로 지정 가능)
@Controller
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form")
public ModelAndView process() {
return new ModelAndView("new-form");
}
}
1. HandlerMapping 을 순서대로 실행하여 핸들러를 찾는다.
2. `RequestMappingHandlerMapping` 이 실행에 성공하여 `@RequestMapping("url")` 이 설정된 컨트롤러를 찾아 반환한다.
4. HandlerAdapter 의 `supprots()` 를 호출하여 핸들러 어댑터를 찾는다.
5. 찾은 `RequestMappingHandlerAdpater` 로 컨트롤러의 `@RequestMapping` 메서드를 실행하고, 결과를 반환한다.
애노테이션 기반 컨트롤러
특징
- 컨트롤러 통합
: @RequestMapping 는 클래스 단위가 아닌 메서드 단위에 적용되었기 때문에, 유연하게 하나로 통합할 수 있다.
- Model 을 사용한 데이터 관리
: Model 객체를 이용하여 데이터를 저장/조회 기능을 제공한다.
- ViewName 직접 반환
: ModelAndView 객체를 반환하는 것이 아니라, String 형식의 뷰의 논리 이름을 반환해도 된다.
RequestMappingHandlerAdater
애노테이션 기반의 컨트롤러의 `@RequestMapping` 을 처리하는 핸들러 어댑터 구조를 자세히 살펴보자
↗ |
① ArgumentResolver |
|||
DispatcherServlet | → → → | RequestMapping HandlerAdapter |
→ → → ② → → → | 핸들러 (컨트롤러) |
↖ |
ReturnValueHandler ③ |
↙ |
① 컨트롤러의 파라미터, 애노테이션 정보를 기반으로 전달 데이터 생성
ex. `HttpServletRequest`, `Model`, `@RequestParam`, `@ModelAttribute`, `@ReuqestBody`, `HttpEntity` 등
② 호출
③ 컨트롤러의 반환값을 변환
ex. `ModelAndView`, `@ResponseBody`, `HttpEntity` 등
ArgumentResolver (HandlerMethodArgumentResolver)
- 다양한 방식의 컨트롤러의 파라미터를 생성하는 역할
- 인터페이스로 제공하여 확장 가능
ReturnValueHandler (HandlerMethodReturnValueHandler)
- 컨트롤러가 반환한 데이터를 적절한 HTTP 응답 형태로 변환하는 역할
- 인터페이스로 제공하여 확장 가능
HTTP 메시지 컨버터 (HttpMessageConverter)
- `ArgumentResolver` 와 `ReturnValueHandler` 가 데이터를 변환할 때 핵심적인 변환 기능을 제공
- 인터페이스로 제공하여 확장 가능
HTTP 메시지 컨버터
HTTP 요청 : `@RequestBody`, `HttpEntity`, `RequestEntity`
HTTP 응답 : `@ResponseBody`, `HttpEntity`, `ResponseEntity`
사용 시에 HTTP 메시지 컨버터가
`요청/응답 데이터의 타입`과 `미디어 타입(Content-Type, Accept)`을 확인하여 변환한다.
기본 메시지 컨버터
0 = ByteArrayHttpMessageConverter
: byte 처리
1 = StringHttpMessageConverter
: 기본 문자 처리
2 = MappingJackson2HttpMessageConverter
: 기본 객체 처리
( 생략 )
대상 클래스 타입, 미디어 타입을 체크해서 사용여부를 결정한다.
ByteArrayHttpMessageConverter
- byte[ ] 데이터를 처리
- 클래스 타입 : `byte[ ]`
- 미디어 타입 : `*/*`
ex. 요청 `@RequestBody byte[] data`
ex. 응답 `@ResponseBody return byte[]` 쓰기 미디어타입 `application/octet-stream` 로 자동 설정
StringHttpMessageConverter
- String 문자로 데이터를 처리
- 클래스 타입 : `String`
- 미디어 타입 : `*/*`
ex. 요청 `@RequestBody String data`
ex. 응답 `@ResponseBody return "ok"` 쓰기 미디어타입 `text/plain` 로 자동 설정
MappingJackson2HttpMessageConverter
- application/json 처리
- 클래스 타입 : `객체` or `HashMap`
- 미디어 타입 : `application/json` 관련
ex. 요청 `@RequestBody HelloData data`
ex. 응답 `@ResponseBody return helloData` 쓰기 미디어타입 `application/json` 관련으로 자동 설정
HTTP 요청 데이터 읽기
1. HTTP 요청이 오고, 컨트롤러에서 `@RequestBody`, `HttpEntity`, `RequestEntity` 파라미터를 사용한다.
2. 메시지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 `canRead()` 를 호출한다.
> 대상 클래스 타입을 지원하는가 ?
ex. `@RequestBody` 의 대상 클래스 (`byte[]`, `String`, `HelloData`)
> HTTP 요청의 Content-Type 미디어 타입을 지원하는가 ?
ex. `text/plain`, `application/json`, `*/*`
3. 위 조건으로 `canRead()` 를 만족하면, `read()` 를 호출하여 객체를 생성하고, 반환한다.
HTTP 응답 데이터 생성
1. 컨트롤러에서 `@ResponseBody`, `HttpEntity`, `ResponseEntity` 로 값이 반환된다.
2. 메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 `canWrite()` 를 호출한다.
> 대상 클래스 타입을 지원하는가 ?
ex. return 의 대상 클래스 (`byte[]`, `String`, `HelloData`)
> HTTP 요청의 Accept 미디어 타입을 지원하는가 ?
ex. `text/plain`, `application/json`, `*/*`
3. 위 조건으로 `canWrite()` 를 만족하면, `write()` 를 호출하여 HTTP 응답 메시지 바디에 데이터를 생성한다.
출처 | 스프링 MVC 1(김영한) - 인프런
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[Spring] SpringMVC 의 요청 매핑과 요청/응답 메시지 기능 ! (0) | 2025.03.20 |
---|---|
[심화] 로깅은 무엇일까 ?? (0) | 2025.03.19 |
[Spring] 리팩토링하며 점진적으로 Spring MVC 프레임워크 만들기 !! (0) | 2025.03.14 |
[Spring] MVC 패턴이 등장하게 된 이유 !! (feat. 서블릿, JSP) (1) | 2025.03.14 |
[Spring] 서블릿의 HttpServletRequest 와 HttpServletResponse 란?? (1) | 2025.03.13 |