김찬진의 개발 블로그
[23/06/06] DispatcherServlet, HandlerMapping, HandlerAdapter, ModelAndView, ViewResolver, View, RequestMappingHandlerAdapter, ArgumentResolver, ReturnValueResolver 본문
1일1배움/Spring (김영한 님)
[23/06/06] DispatcherServlet, HandlerMapping, HandlerAdapter, ModelAndView, ViewResolver, View, RequestMappingHandlerAdapter, ArgumentResolver, ReturnValueResolver
kim chan jin 2023. 6. 6. 20:49개념들이 헷갈려 위 개념들을 포함한 스토리를 만들어서 메커니즘을 정리해보았습니다.
1. 클라이언트의 요청이 들어오면 DispatcherServlet이 요청을 수신합니다.
2. DispatcherServlet은 HandlerMapping에게 요청을 전달하여 적절한 handler(=controller)를 찾습니다.
3. HandlerMapping은 요청을 기반으로 적절한 handler(=controller)를 찾아 반환합니다.
4. DispatcherServlet은 반환된 handler(=controller)를 실행할 수 있는 적절한 HandlerAdapter에게 전달합니다. 스프링은 HandlerAdapter 인터페이스를 구현한 다양한 클래스들을 갖고 있습니다. 예를 들어 만약 @RequestMapping 애노테이션을 사용하는 handler(=controller)의 경우, RequestMappingHandlerAdapter에게 전달되고, 만약 @RestController 애노테이션을 사용하는 handler(=controller)의 경우, RequestMappingHandlerAdapter에게 전달됩니다.
@RequestMapping(그 자식 애노테이션 @PostMapping, @GetMapping 등도 포함) 애노테이션을 사용하는 handler(=controller)의 경우, handler(=controller)는 RequestMappingHandlerAdpater을 호출하여 handler(=controller)가 정상작동하기 위한 작업을 수행하도록 합니다. 그 작업의 예시는 다음과 같습니다.
(Request, Response 모두 3가지 방법이 있습니다.)
<요청>
1. 쿼리 파라미터에 데이터를 포함해서 GET 요청
( @RequestParam 단순타입 / @RequestParam Map / @RequestParam MultiValueMap / @ModelAttribute )
2. HTML Form = HTTP 메세지 바디에 쿼리 파라미터로 POST 요청
( @RequestParam 단순타입 / @RequestParam Map / @RequestParam MultiValueMap / @ModelAttribute )
3-1. HTTP message body에 문자열 데이터를 직접 담아서 요청
(@RequestBody 단순타입/ @RequestBody 객체 / HttpEntity)
3-2. HTTP message body에 JSON 데이터를 직접 담아서 요청
(@RequestBody 단순타입 / @RequestBody 객체 / HttpEntity)
<응답>
1. 정적 리소스
2. 뷰 템플릿 (단, 반환값이 문자열일 경우 @ResponseBody 붙지 않아야 view로 인식)
(ModelAndView / 문자열 / void)
3. HTTP(REST) API, HTTP Message Body에 직접 입력 (단, 반환값이 문자열일 경우 @ResponseBody 붙어야 문자열로 인식)
(ResponseEntity(문자열) / 문자열 / ResponseEntity(객체) / 객체)
<요청 detail>
1,2. 만약 handler(=controller)의 인자가 "@RequestParam 단순타입" 이라면, RequestMappingHandlerAdapter가 호출됩니다. RequestMappingHandlerAdapter는 ArgumentResolver를 호출합니다. ArgumentsResolver는 RequestParamMethodArgumentResolover를 호출합니다. RequestParamMethodArgumentResolver는 @RequestParam 애노테이션을 해석하고, 요청 파라미터를 해당 handler(=controller) 메소드의 매개변수와 매핑하여 값을 전달합니다.
1,2. 만약 handler(=controller)의 인자가 "@RequestParam Map" 이라면, RequestMappingHandlerAdapter가 호출됩니다. RequestMappingHandlerAdapter는 ArgumentResolver를 호출합니다. ArgumentsResolver는 RequestParamMapMethodArgumentResolover를 호출합니다. RequestParamMapMethodArgumentResolver는 @RequestParam 애노테이션을 해석하고, 요청 파라미터를 Map 형태로 전달하며, Map을 해당 handler(=controller) 메소드의 매개변수와 매핑하여 값을 전달합니다.
1,2. 만약 handler(=controller)의 인자가 "@RequestParam MultiValueMap" 이라면, RequestMappingHandlerAdapter가 호출됩니다. RequestMappingHandlerAdapter는 ArgumentResolver를 호출합니다. ArgumentsResolver는 RequestParamMapMethodArgumentResolover를 호출합니다. RequestParamMapMethodArgumentResolver는 @RequestParam 애노테이션을 해석하고, 요청 파라미터를 MultiValueMap 형태로 전달하며, MultiValueMap 을 해당 handler(=controller) 메소드의 매개변수와 매핑하여 값을 전달합니다.
1,2. 만약 handler(=controller)의 인자가 "@ModelAttribute" 이라면, RequestMappingHandlerAdapter가 호출됩니다. RequestMappingHandlerAdapter는 ArgumentsResolver를 호출합니다. ArgumentsResolver는 ModelAttributeMethodProcessor를 호출합니다. ModelAttributeMethodProcessor는 @ModelAttribute 애노테이션을 해석하고, 해당 객체를 생성하고 초기화합니다. 요청 파라미터의 key에 따라 객체의 프로퍼티를 찾습니다. 객체의 프로퍼티가 존재한다면 해당 프로퍼티의 setter를 호출하여 객체의 프로퍼티에 요청 파라미터의 value를 바인딩합니다. 객체를 해당 handler(=controller) 메소드의 매개변수와 매핑하여 값을 전달합니다.
3-1,3-2. 만약 handler(=controller)의 인자가 "@RequestBody 단순타입" 이라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ArgumentsResolver를 호출합니다. ArgumentsResolver는 HttpMessageConverter를 호출합니다.HttpMessageConverter는 StringHttpMessageConverter를 호출합니다. StringHttpMessageConverter는 요청 메세지의 문자열 데이터를 단순 타입으로 변환합니다. 이 단순 타입 값을 해당 handler(=controller) 메소드의 매개변수와 매핑하여 값을 전달합니다. 이후 참조변수 = objectMapper.readValue(문자열, 객체.class)를 작성하여 직접 매핑하여 객체를 생성합니다.
3-1,3-2. 만약 handler(=controller)의 인자가 "@RequestBody 객체" 라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ArgumentsResolver를 호출합니다. ArgumentsResolver는 HttpMessageConverter를 호출합니다. HttpMessageConverter는 MappingJackson2HttpMessageConverter를 호출합니다. MappingJackson2HttpMessageConverter는 메세지바디에 실려 요청으로 들어온 JSON을 객체의 각 프로퍼티에 매핑 하여 객체를 생성합니다. 생성된 객체를 해당 handler(=controller) 메소드의 매개변수와 매핑하여 값을 전달합니다.
3-1,3-2. 만약 handler(=controller)의 인자가 "HttpEntity" 라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ArgumentsResolver를 호출합니다. ArgumentsResolver는 HttpEntityMethodProcessor를 호출합니다. HttpEntityMethodProcessor는 요청의 헤더, 바디, 상태 등의 정보를 추출하여 HttpEntity 객체를 생성합니다. 생성된 HttpEntity 객체는 hanlder(=controller) 메소드의 매개변수와 매핑하여 값을 전달합니다.
<응답 detail>
2. 만약 handler(=controller)의 반환값이 "ModelAndView"이라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ReturnValueResolver를 호출합니다. ReturnValueResolver는 ModelAndViewResolver를 호출합니다. ModelAndViewResolver는 handler(=controller)에서 반환된 ModelAndView 객체를 처리하여 뷰 이름과 모델 데이터를 추출합니다. 이후에 뷰 이름과 모델 데이터를 템플릿 엔진 등에 전달하여 최종적인 응답을 생성합니다.
2. 만약 handler(=controller)의 반환값이 "문자열"이라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ReturnValueResolver를 호출합니다. ReturnValueResolver는 StringReturnValueHandler를 호출합니다. StringReturnValueHandler는 handler(=controller)에서 반환된 문자열을 그대로 응답으로 사용합니다.
2. 만약 handler(=controller)의 반환값이 "void"라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ReturnValueResolver를 호출합니다. ReturnValueResolver는 VoidReturnValueHandler를 호출합니다. VoidReturnValueHandler는 반환값이 없는 경우에는 별도의 처리를 하지 않으며, 응답은 생성되지 않습니다.
3. 만약 handler(=controller)의 반환값이 "ResponseEntity(문자열)"이라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ReturnValueResolver를 호출합니다. ReturnValueResolver는 ResponseEntityReturnValueHandler를 호출합니다. ResponseEntityReturnValueHandler는 handler(=controller)에서 반환된 ResponseEntity 객체를 처리하여 해당하는 상태 코드, 헤더, 바디 등을 포함한 응답을 생성합니다.
3. 만약 handler(=controller)의 반환값이 "문자열"라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ReturnValueResolver를 호출합니다. ReturnValueResolver는 HttpMessageConverter를 호출합니다. HttpMessageConverter는 StringHttpMessageConverter를 호출하여 문자열을 응답으로 사용합니다.
3. 만약 handler(=controller)의 반환값이 "ResponseEntity(객체)"라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ReturnValueResolver를 호출합니다. ReturnValueResolver는 ResponseEntityReturnValueHandler를 호출합니다. ResponseEntityReturnValueHandler는 handler(=controller)에서 반환된 ResponseEntity 객체를 처리하여 해당하는 상태 코드, 헤더, 바디 등을 포함한 응답을 생성합니다.
3. 만약 handler(=controller)의 반환값이 "객체"라면, handler(=controller)는 RequestMappingHandlerAdapter를 호출합니다. RequestMappingHandlerAdapter는 ReturnValueResolver를 호출합니다. ReturnValueResolver는 HttpMessageConverter를 호출합니다. HttpMessageConverter는 MappingJackson2HttpMessageConverter를 호출합니다. MappingJackson2HttpMessageConverter는 객체를 JSON으로 변환하여 반환합니다.
(@RequestBody는 메세지 바디에 싣는 방식, @ModelAttribute는 쿼리 파라미터에 싣는 방식이라는 점이 차이이고, 만약 @RequestBody를 생략한다면 스프링은 handler(=controller)의 인자를 @RequestBody 객체가 아니라 @ModelAttribute 객체로 인식하여 쿼리 파라미터를 찾게 됩니다. 하지만 쿼리 파라미터가 아니라 메세지 바디에 실는 방식을 의도했기 때문에 객체가 생성되지 않아 None이 출력됩니다.)
5. HandlerAdapter구현체가 전달받은 handler(=controller)가 실행될 수 있도록 요청 값 변환, 검증, 반환 값 변환 등의 작업을 수행했다면, 그 결과를 handler(=controller)에게 전달하고, handler(=controller)는 비즈니스 로직 수행을 완료하고, 필요한 데이터를 Model에 저장합니다.
6. handler(=controller)는 ModelAndView 객체를 생성하고, Model를 ModelAndView에 추가합니다.
7. ModelAndView 객체는 ViewResolver에게 전달됩니다.
8. ViewResolver는 논리적인 View(ex. "myForm")를 물리적인 View(ex. "WEB/views/myForm.jsp")로 변환합니다.
9.. 변환된 View는 DispatcherServlet에 반환됩니다.
10. DispatcherServlet은 View를 실행하여 클라이언트에게 응답을 생성합니다.
11. 응답은 클라이언트에게 전송되고, 요청-응답 주기가 완료됩니다.
'1일1배움 > Spring (김영한 님)' 카테고리의 다른 글
[23/09/04] 트랜잭션 하나로 묶기, 트랜잭션 하나에 참여하기(전파) (1) | 2023.09.04 |
---|---|
[23/08/31] Spring 이 체크예외는 커밋, 언체크예외는 롤백하는 이유 (0) | 2023.08.31 |
[23/05/03] 서블릿 객체와 서블릿 내장 객체 (0) | 2023.05.03 |
[23/05/01] Servlet / JSP / (0) | 2023.05.02 |
[23/04/25] 진짜 객체가 컨테이너에 등록되기 전에는 프록시 객체 사용을 조심하자 (0) | 2023.04.25 |
Comments