HttpMessageConverter란?
웹 브라우저 같은 클라이언트에서 보여지는 HTML 컨텐츠가 렌더링(Rendering)되는 방식은 크게 두 가지입니다.
하나는 웹 애플리케이션 서버에서 동적으로 변하는 데이터를 포함하는 HTML을 만들어서 HTML 자체를 한번에 클라이언트 쪽으로 내려주는 방식입니다.
이 방식은 JSP나 타임리프(Thymeleaf) 같은 기술을 사용해서 HTML을 템플릿화 한 다음에 Controller의 핸들러 메서드에서 리턴하는 모델 데이터를 템플릿에 동적으로 채워 넣은 후, 최종적으로 완성된 HTML을 클라이언트 쪽으로 내려주는 방식입니다. 이 방식을 바로 서버 사이드 렌더링(Server Side Rendering)이라고 합니다.
또 하나는 클라이언트 쪽을 담당하는 Apache나 NginX 같은 웹 서버에 HTML을 올려 놓은 후, 자바스크립트의 Ajax 기술을 이용해서 웹 애플리케이션 서버에 데이터를 요청 합니다.
그리고 응답으로 전달받은 데이터를 웹 서버에 올려둔 HTML에 동적으로 채워 넣은 후, 최종적으로 완성된 HTML을 클라이언트 쪽으로 내려주는 방식입니다. 이 방식을 바로 클라이언트 사이드 렌더링(Client Side Rendering)이라고 합니다.
위 두가지 방식 중에서 HttpMessageConverter는 두 번째 방식인 클라이언트 사이드 렌더링과 관련이 있습니다.
즉, 클라이언트 사이드 렌더링을 위해 필요한 데이터를 특정 형식으로 변환해 주는 것이 바로 HttpMessageConverter
입니다.
HttpMessageConverter의 사용 목적
HttpMessageConverter
는 크게 두 가지 목적으로 사용됩니다.
하나는 웹 서버에서 전송된 Reqeust Body를 DTO 같은 클래스의 객체로 변환해서 웹 애플리케이션 서버 쪽에서 사용할 수 있도록 해주는 것이고,
다른 하나는 웹 서버 쪽으로 전달할 응답 데이터를 Response Body로 변환해주는 것입니다.
HttpMessageConverter의 동작 과정
[그림 1-1]은 클라이언트의 요청을 처리하는 Spring MVC의 기본 동작 과정입니다.
이 Spring MVC의 동작 과정에서 HandlerAdapter의 동작 과정을 조금 더 구체적으로 설명하겠습니다.
요청 처리 시 HandlerAdapter의 동작 과정
- HandlerMapping을 통해 적절한 HandlerAdapter를 찾으면 HandlerAdapter는 Controller로 넘겨줄 파라미터를 결정하기 위해 이 작업을 HandlerMethodArgumentResolver에게 위임합니다.
- HandlerMethodArgumentResolver는 HttpMessageConverter에게 HTTP Request Body를 특정 타입의 객체로 변환해주기를 요청합니다.
- HttpMessageConverter는 HTTP Request Body를 특정 타입의 객체로 변환합니다.
- HandlerMethodArgumentResolver는 변환된 데이터를 전달 받아서 이 데이터를 다시 HandlerAdapter에게 전달합니다.
- HandlerAdapter는 HandlerMethodArgumentResolver로부터 전달 받은 데이터를 핸들러 메서드의 파라미터로 포함 시킨 후, 핸들러 메서드를 호출합니다.
응답 처리 시 HandlerAdapter의 동작 과정
- 핸들러 메서드가 응답으로 전달할 데이터를 리턴합니다.
- HandlerMethodReturnValueHandler는 핸들러 메서드로부터 전달 받은 응답 데이터를 HttpMessageConverter에게 전달합니다.
- HttpMessageConverter는 HandlerMethodReturnValueHandler로부터 전달 받은 데이터를 HTTP Response Body에 포함되는 형식의 데이터로 변환합니다.
- HandlerMethodReturnValueHandler는 HttpMessageConverter로부터 전달 받은 데이터를 HandlerAdapter에게 전달합니다.
REST API에서의 HttpMessageConverter
Spring MVC에서는 기본적으로 아래의 HttpMessageConverter 구현체가 활성화 되어 있습니다.
- ByteArrayHttpMessageConverter
- StringHttpMessageConverter
- ResourceHttpMessageConverter
- SourceHttpMessageConverter
- FormHttpMessageConverter
- Jaxb2RootElementHttpMessageConverter
- MappingJackson2HttpMessageConverter
- MappingJacksonHttpMessageConverter
- AtomFeedHttpMessageConverter
- RssChannelHttpMessageConverter
만약 우리가 HTTP Request 또는 Response를 위한 통신 프로토콜로 JSON을 사용하는 REST API Controller를 구현했다고 가정해 봅시다.
특정 Controller의 핸들러 메서드 파라미터에 @RequestBody 애너테이션이 추가되거나 파라미터의 타입이 HttpEntity라면 위에서 나열한 HttpMessageConverter가 순서대로 HTTP Request Body를 체크해서 핸들러 파라미터에 지정한 클래스로 변환이 되는지, HTTP Message의 Content Type을 지원하는지 여부를 확인하게 됩니다.
이 두가지 조건을 모두 만족한다면 아마도 MappingJackson2HttpMessageConverter가 Request Body를 해당 클래스의 객체로 변환할 것이고, 조건에 부합하는 HttpMessageConverter가 존재하지 않는다면 예외가 발생할 것입니다.
이제 응답 데이터를 JSON 형식으로 전달하는 상황을 생각해 봅시다.
핸들러 메서드에 @ResponseBody 애너테이션을 추가하거나 핸들러 메서드의 리턴 타입이 HttpEntity라면 역시 위에서 나열한 HttpMessageConverter가 순서대로 핸들러 메서드의 리턴 값을 체크해서 해당 클래스 타입을 지원하는지, Accept Header에 명시된 MediaType으로 변환이 되는지 여부를 확인하게 됩니다.
HttpMessageConverter가 동작하는 시점
HttpMessageConverter가 동작하는 시점은 JSON을 DTO로 deserialization 할 때와 DTO를 JSON으로 serialization 할 때가 다릅니다. 아래는 HttpMessageConverter
의 대략적인 동작 흐름입니다. 참고하세요.
JSON -> DTO로 deserialization
HandlerMethodArgumentResolverComposite.resolveArgument()
→ RequestResponseBodyMethodProcessor.resolveArgument()
→ RequestResponseBodyMethodProcessor.readWithMessageConverters()
> converter.read()
→ objectReader.readValue()
DTO -> JSON serialization
HandlerMethodReturnValueHandlerComposite.handleReturnValue()
→ HttpEntityMethodProcessor.handleReturnValue()
→ AbstractMessageConverterMethodProcessor.writeWithMessageConverters()
> genericConverter.write()
→ AbstractGenericHttpMessageConverter.write()
→ AbstractJackson2HttpMessageConverter.writeInternal()
> objectWriter.writeValue(generator, value)
→ write() and flush() 과정을 거친다.
→ ...
→ ...
→ CoyoteAdapter.service()
> response.finishResponse()
// response가 close되면서 클라이언트 쪽에 출력
HttpMessageConverter 참고 자료
'Java Backend 개발자 되기 > Spring MVC' 카테고리의 다른 글
그림과 코드로 알아보는 Spring MVC 요청 처리 흐름 (2) | 2023.01.16 |
---|---|
Mac OS에서 시스템 환경 변수 등록하는 방법 (1) | 2022.11.24 |
ResponseEntity 알아보기 (2) | 2022.04.21 |
Controller 핸들러 메서드의 Argument 알아보기 (0) | 2022.04.19 |
@RequestMapping 애너테이션 (0) | 2022.04.19 |