해결된 질문
작성
·
408
·
수정됨
2
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예
[질문 내용]
안녕하세요 !!
[1] 요청 시에 @RequestBody와 HttpEntity를 안 쓰는 경우 , 응답 시에 @ResponseBody와 HttpEntity를 안 쓰는 경우
[2] 요청 시에 @RequestBody와 HttpEntity를 쓰는 경우 , 응답 시에 @ResponseBody와 HttpEntity를 쓰는 경우
이 두 가지 케이스로 나누어 스프링 프레임워크의 동작 구조를 정리해보았는데 제가 이해하는 게 맞는지 너무 길지만 한번만 피드백해주시면 감사하겠습니다 !!
[1]
요청 : 파라메터 타입에 @RequestBody X 이거나 HttpEntity X의 경우
응답 : 반환 값에 @ResponseBody X 이거나 HttpEntity X의 경우
1. 클라이언트의 요청
2. DispatcherServlet를 호출(urlPatterns = /* 경로이기 때문)
3. HandlerMapping의 가장 우선순위에 있는 구현체인 RequestMappingHandlerMapping을 통해 @Controller이 붙은 클래스의 객체를 매핑 정보로 활용
4. doDispatch()의 getHandler()에서 해당 매핑 정보의 컨트롤러 객체 반환
5. doDispatch()의 getHandlerAdapter()을 통해 HandlerAdapter을 호출 후 컨트롤러를 처리할 수 있는 어댑터 있는지 검증(supports()) 후 어댑터 호출(handle())
6. 이때 컨트롤러는 @Controller의 @RequestMapping이 붙어있으므로 HandlerAdapter의 가장 우선순위의 RequestMappingHandlerAdapter 어댑터 구현체가 호출되는 것!
7. 어댑터는 컨트롤러의 파라메터에 해당되는 객체가 ArgumentResolver의 구현체로 존재하는지 검증(supportsParameter()) 후 존재하면 생성(resolveArgument())
8. 어댑터는 생성된 객체를 컨트롤러의 파라메터에 주입하면서 컨트롤러 호출
9. 컨트롤러는 로직 수행 후 return 값(객체)을 어댑터에 반환
10. 어댑터는 ResultValueHandler 호출하면서 반환값에 해당되는 객체가 존재하는지 검증(supportsReturnType()) 후 존재하면 생성(handleReturnValue())
11. 어댑터는 생성된 객체 및 논리적 뷰 이름으로 초기화된 ModelAndView 객체 생성 후 DispatchServlet에 반환
12. DispatchServlet에서 ViewResolver에 논리적 뷰 이름을 넘겨주면서 호출
13. ViewResolver에선 논리적 뷰 이름을 물리적 뷰 경로로 바꿔주고 그 값으로 초기화된 View 객체 반환
14. DispatchServlet에서 View객체 이용해서 render() 호출
15. JSP 뷰 템플릿이었으면 render()에서 JSP 코드로 포워드 후 랜더링하고 나머지 타임리프 같은 뷰 템플릿이면 render() 받자마자 바로 화면 랜더링
[2]
요청 : 파라메터 타입에 @RequestBody O 이거나 HttpEntity O의 경우
응답 : 반환 값에 @ResponseBody O 이거나 HttpEntity O의 경우
1. 클라이언트의 요청
2. DispatcherServlet를 호출(urlPatterns = /* 경로이기 때문)
3. HandlerMapping의 가장 우선순위에 있는 구현체인 RequestMappingHandlerMapping을 통해 @Controller이 붙은 클래스의 객체를 매핑 정보로 활용
4. doDispatch()의 getHandler()에서 해당 매핑 정보의 컨트롤러 객체 반환
5. doDispatch()의 getHandlerAdapter()을 통해 HandlerAdapter을 호출 후 컨트롤러를 처리할 수 있는 어댑터 있는지 검증(supports()) 후 어댑터 호출(handle())
6. 이때 컨트롤러는 @Controller의 @RequestMapping이 붙어있으므로 HandlerAdapter의 가장 우선순위의 RequestMappingHandlerAdapter 어댑터 구현체가 호출되는 것!
7. 어댑터는 컨트롤러의 파라메터에 해당되는 타입의 객체가 ArgumentResolver의 구현체로 존재하는지 검증(supportsParameter())
8. ArgumentResovler이 검증하던 중 컨트롤러의 파라메터 타입이 @RequestBody 혹은 HttpEntity임을 감지하고 RequestResponseBodyMethodProcessor 구현체가 동작하며 HTTP 메시지 컨버터 호출
9. RequestResponseBodyMethodProcessor 구현체는 HTTP 메시지 컨버터의 canRead()를 통하여 파라메터의 클래스 타입과 미디어 타입(Content-Type)을 검증하고 조건 만족하면 read()를 통해 HTTP 메세지 바디에 있는 데이터 변환
10. RequestResponseBodyMethodProcessor 구현체는 변환된 객체를 어댑터에 반환
11. 어댑터에선 반환된 객체를 컨트롤러의 파라메터에 주입하면서 컨트롤러 호출
12. 컨트롤러는 로직 수행 후 return 값(객체)을 어댑터에 반환
13. 어댑터는 ResultValueHandler 호출하면서 해당 반환값이 존재하는지 검증(supportsReturnType())
14. 이때 ResultValueHandler에선 컨트롤러의 반환값이 @ResponseBody , HttpEntity임을 감지 후 RequestResponseBodyMethodProcessor 구현체가 동작하며 HTTP 메시지 컨버터를 호출
15. RequestResponseBodyMethodProcessor구현체는 HTTP 메시지 컨버터의 canWrite()를 통하여 파라메터 클래스 타입과 HTTP 요청 메시지에서의 미디어 타입(Accept)을 검증하고 조건 만족하면 write() 수행
16. write()에선 데이터를 변환하여 HTTP 응답 메세지 바디에 삽입 후 응답 메세지 클라이언트에게 반환
감사합니다 !! 그리고 만약 제가 정리한 부분이 맞다면 HTTP API 방식으로 동작하는 경우 맨 마지막 16번에서 write() 이후에 만들어진 응답 메세지를 어디서 클라이언트에게 반환하는 건지 궁금합니다 !
답변 1
2
안녕하세요. 경민님
생각하신 내용이 맞습니다.
추가로 16번 질문하신 부분은 RequestResponseBodyMethodProcessor 내부에서 더 찾아가보면
AbstractHttpMesageConverter까지 이어지는데, 여기서 write 메서드가 호출되고 그 내부에서 각각의 컨버터에 있는 실제 write가 호출됩니다.
StringHttpMessageConverter의 경우 writeInternal() 메서드 내부에서 실제 write가 발생합니다.
여기서 HttpResponse에 스트림으로 응답 데이터를 전달하게 됩니다.
감사합니다.
매번 감사드립니다
선생님께서 잘 가르쳐주신 덕분에 전반적인 스프링 프레임워크 구조를 이해할 수 있었습니다