๐ง HttpMessageConverter
@RequestBody๋ @ResponseBody๋ ๋ฉ์์ง ๋ฐ๋์ ๋ค์ด์๊ฑฐ๋ ๋ฉ์ธ์ง ๋ฐ๋์ ์์ฑ๋๋ ๋ฌธ์ ํน์ JSON ๋ฑ์ ๋ฐ์ดํฐ๋ฅผ String ๋๋ ๊ฐ์ฒด๋ก ๋ฐ๋ก ๋ณํํ์ฌ ์ฌ์ฉํ ์ ์๊ฒ ํด์ค๋๋ค.
์ด์ ๊ฐ์ด HTTP ๋ฉ์์ง ๋ฐ๋์ ๋ด์ฉ์ ๊ฐ๋ฐ์๊ฐ ์ฌ์ฉํ๊ฒ ํธ๋ฆฌํ๋๋ก ๋ณํํ์ฌ ์ฃผ๋ ์ญํ ์ HttpMessageConverter๊ฐ ๋ด๋นํฉ๋๋ค.
HttpMessageConverter๋ @RequestBody, HttpEntity๊ฐ ํ๋ผ๋ฏธํฐ์ ๋ถ์ด์๋ ๊ฒฝ์ฐ, ๊ทธ๋ฆฌ๊ณ ๋ฐํ ์ @ResponseBody ํน์ HttpEntity๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์๋ํฉ๋๋ค.
๐ง HttpMessageConverter ๊ตฌ์กฐ
HTTP ๋ฉ์์ง ์ปจ๋ฒํฐ๋ ์ธํฐํ์ด์ค๋ก ์ด๋ฃจ์ด์ ธ ์์ต๋๋ค.
public interface HttpMessageConverter<T> {
/**
* ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ํด๋น ํด๋์ค, ๋ฏธ๋์ดํ์
์ ์ง์ํ๋์ง ์ฒดํฌ
*/
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
/**
* ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ํด๋น ํด๋์ค, ๋ฏธ๋์ดํ์
์ ์ง์ํ๋์ง ์ฒดํฌ
*/
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return (canRead(clazz, null) || canWrite(clazz, null) ?
getSupportedMediaTypes() : Collections.emptyList());
}
/**
* ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ํตํด ๋ฉ์์ง๋ฅผ ์ฝ์
*/
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
/**
* ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ํตํด ๋ฉ์์ง๋ฅผ ์
*/
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
๐ง ์คํ๋ง ๋ถํธ์์ ๋ฑ๋กํ๋ ๊ธฐ๋ณธ ๋ฉ์์ง ์ปจ๋ฒํฐ
์ด๊ฒ๋ค๋ณด๋ค ๋ ๋ง์ ๋ฉ์์ง ์ปจ๋ฒํฐ๋ฅผ ๋ฑ๋กํ์ง๋ง, ๋ํ์ ์ธ ์ปจ๋ฒํฐ๋ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๐ ByteArrayHttpMessageConverter
- byte[] ํ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
- ํด๋์ค ํ์ : byte[], ๋ฏธ๋์ด ํ์ : */*
- ์์ฒญ ์) @RequestBody byte[] data
- ์๋ต ์) @ResponseBody return byte[], ์ฐ๊ธฐ ๋ฏธ๋์ดํ์ : application/octet-stream
๐ StringHttpMessageConverter
- String ๋ฌธ์๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
- ํด๋์ค ํ์ : String, ๋ฏธ๋์ด ํ์ : */*
- ์์ฒญ ์) @RequestBody String data
- ์๋ต ์) @ResponseBody return "ok", ์ฐ๊ธฐ ๋ฏธ๋์ดํ์ : test/plain
๐ MappingJackson2HttpMessageConverter
- application/json์ ์ฒ๋ฆฌํฉ๋๋ค.
- ํด๋์ค ํ์ : ๊ฐ์ฒด ๋๋ HashMap, ๋ฏธ๋์ด ํ์ : application/json
- ์์ฒญ ์) @RequestBody HelloData data
- ์๋ต ์) @ResponseBody return helloData, ์ฐ๊ธฐ ๋ฏธ๋์ดํ์ : application/json
๐ง ๋์ ์๋ฆฌ ์ดํด๋ณด๊ธฐ
๐ HTTP ์์ฒญ ๋ฐ์ดํฐ ์ฝ๊ธฐ
์ปจํธ๋กค๋ฌ์์ @RequestBody, HttpEntity ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ ์ํ์์ HTTP ์์ฒญ์ด ์จ ๊ฒฝ์ฐ, ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ๋ฉ์์ง๋ฅผ ์ฝ์ ์ ์๋์ง ํ์ธํ๊ธฐ ์ํด canRead()๋ฅผ ํธ์ถํฉ๋๋ค.
๋ค์ ๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋์ง ํ์ธํฉ๋๋ค.
- ๋์ ํด๋์ค ํ์
์ ์ง์ํ๋๊ฐ.
- ์) @RequestBody์ ๋์ ํด๋์ค : byte[], String, HelloData
- HTTP ์์ฒญ์ Content-Type ๋ฏธ๋์ด ํ์
์ ์ง์ํ๋๊ฐ
- ์) test/plain, application/json, */*
canRead()์กฐ๊ฑด์ ๋ง์กฑํ๋ฉด read()๋ฅผ ํธ์ถํด์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๋ฐํํฉ๋๋ค.
๐ HTTP ์๋ต ๋ฐ์ดํฐ ์์ฑ
@ResponseBody, HttpEntity๋ก ์ปจํธ๋กค๋ฌ์์ ๊ฐ์ ๋ฐํํ๋ ๊ฒฝ์ฐ, ๋ฉ์์ง ์ปจ๋ฒํฐ๊ฐ ๋ฉ์์ง๋ฅผ ์ธ ์ ์๋์ง ํ์ธํ๊ธฐ ์ํด canWrite()๋ฅผ ํธ์ถํฉ๋๋ค.
๋ค์ ๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋์ง ํ์ธํฉ๋๋ค.
- ๋์ ํด๋์ค ํ์
์ ์ง์ํ๋๊ฐ
- ์) return์ ๋์ ํด๋์ค (byte[], String, HelloData)
- HTTP ์์ฒญ์ Accept ๋ฏธ๋์ด ํ์
์ ์ง์ํ๋๊ฐ (๋ ์ ํํ๋ @RequestMapping์ produce)
- ์) test/plain, application/json, */*
canWrite() ์กฐ๊ฑด์ ๋ง์กฑํ๋ฉด write()๋ฅผ ํธ์ถํด์ HTTP ์๋ต ๋ฉ์์ง ๋ฐ๋์ ๋ฉ์์ง๋ฅผ ์์ฑํฉ๋๋ค.
๐ง HttpMessageConverter ์ฌ์ฉ์์น
์ด์ ๊ธ๋ค์์ ์ดํด๋ณธ ์คํ๋ง MVC์ ๊ตฌ์กฐ๋ฅผ ๋ค์ ํ๋ฒ ํ์ธํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
(์ฌ์ง์ ๊น์ํ ์ ์๋์ ์คํ๋ง MVC 1ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํต์ฌ ๊ธฐ์ ์ ์๋ฃ์์ ๋ฐ์ทํ์์ต๋๋ค.)
HttpMessageConverter๋ ์ ๋ ธํ ์ด์ ๊ธฐ๋ฐ์ ์ปจํธ๋กค๋ฌ,
์ฆ @RequestMapping์ ์ฒ๋ฆฌํ๋ ํธ๋ค๋ฌ ์ด๋ํฐ์ธ RequestMappingHandlerAdapter์์ ์ฌ์ฉํฉ๋๋ค.
๊ทธ ์ ์ ์ ์ HandlerMethodArgumentResolver์ HandlerMethodReturnValueHandler์ ๋ํด ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๐ง HandlerMethodArgumentResolver
์ ๋ ธํ ์ด์ ๊ธฐ๋ฐ์ ์ปจํธ๋กค๋ฌ๋ ๋งค์ฐ ๋ค์ํ ํ๋ผ๋ฏธํฐ(HttpServletRequest, @RequestParam, @ModelAttribute, @RequestBody, HttpEntity ๋ฑ๋ฑ)๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
(๋ ๊ถ๊ธํ๋ค๋ฉด ๋ค์ ์ฌ์ดํธ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์)
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-annarguments
์ด๋ฌํ ๊ฒ์ด ๊ฐ๋ฅํ ์ด์ ๊ฐ ๋ฐ๋ก HandlerMethodArgumentResolver๋๋ถ์ ๋๋ค.
์ ๋ ธํ ์ด์ ๊ธฐ๋ฐ ์ปจํธ๋กค๋ฌ๋ฅผ ์ฒ๋ฆฌํ๋ RequestMappingHandlerAdapter๋ ์ปจํธ๋กค๋ฌ๋ฅผ ํธ์ถํ๊ธฐ ์ ์ ArgumentResolver๋ฅผ ์ฌ์ฉํด์ ๋ค์ด์จ ์์ฒญ ์ ๋ณด๋ฅผ ์ปจํธ๋กค๋ฌ๊ฐ ํ์๋ก ํ๋ ๋ฐ์ดํฐ ํ์์ผ๋ก ๋ณํํด์ค๋๋ค.
๊ทธ๋ฆผ์ผ๋ก ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๐ง HandlerMethodReturnValueHandler
์๋ต ๋ฐ์ดํฐ๋ฅผ ์ ์กํ ๋๋ HandlerMethodReturnValueHandler๋ฅผ ์ฌ์ฉํด์ ์ปจํธ๋กค๋ฌ๊ฐ ๋ฐํํ ๋ฐ์ดํฐ๋ฅผ ๋ณํ์์ผ์ ๋ฐํํด์ค๋๋ค.
(๊ฐ๋ฅํ ์๋ต ๊ฐ์ ๋ค์ ๊ณต์ ๋ฉ๋ด์ผ์์ ํ์ธํ ์ ์์ต๋๋ค.)
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
๐ง HttpMessageConverter ์ฌ์ฉ์์น
๋ค์ HttpMessageConverter๋ก ๋์์์,
๊ฒฐ๊ตญ HttpMessageConverter๋ HandlerMethodArgumentResolver์ HandlerMethodReturnValueHandler์์ ์ฌ์ฉ๋ฉ๋๋ค.
HttpMessageConverter๋ฅผ ์ฌ์ฉํ๋ @RequestBody ์ญ์๋ ์ปจํธ๋กค๋ฌ๊ฐ ํ์๋ก ํ๋ ํ๋ผ๋ฏธํฐ์ ์ฌ์ฉ๋ฉ๋๋ค.
@ResponseBody๋ ์ปจํธ๋กค๋ฌ์ ๋ฐํ ๊ฐ์์ ์ฌ์ฉ๋ฉ๋๋ค.
์ฆ ์์ฒญ์ ๊ฒฝ์ฐ @RequestBody๋ฅผ ์ฒ๋ฆฌํ๋ HandlerMethodArgumentResolver๊ฐ ์์ผ๋ฉฐ, HttpEntity๋ฅผ ์ฒ๋ฆฌํ๋ HandlerMethodArgumentResolver๊ฐ ์์ต๋๋ค.
ํด๋น HandlerMethodArgumentResolver๋ค์์ HttpMessageConverter๋ฅผ ์ฌ์ฉํด์ ํ์ํ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ํ๋์ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
ArgumentResolver, ReturnValueHandler๋ค์ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์์ ๋ค์ด ์ง์ ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๋ ์์ ์ ์ฒ๋ฆฌํ์ง๋ง, @RequestBody, @ResponseBody, HttpEntity๋ฅผ ์ฌ์ฉํ๋ฉด ๋ด๋ถ์์ HttpMessageConverter๋ฅผ ์ฌ์ฉํ์ฌ ๋ณํํฉ๋๋ค.
์คํ๋ง MVC๋ @RequestBody, @ResponseBody๊ฐ ์์ผ๋ฉด RequestResponseBodyMethodProcessor(HandlerMethodArgumentResolver),
HttpEntity๊ฐ ์์ผ๋ฉด HttpEntityMethodProcessor(HandlerMethodReturnValueHandler)๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๐ง ๊ธฐ๋ฅ ํ์ฅ
์คํ๋ง์ HandlerMethodArgumentResolver์ HandlerMethodReturnValueHandler, HttpMessageConverter๋ฅผ ๋ชจ๋ ์ธํฐํ์ด์ค๋ก ์ ๊ณตํ๋ฏ๋ก, ํ์์ ๋ฐ๋ผ์ ์ธ์ ๋ ์ง ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์์ต๋๋ค.
๊ธฐ๋ฅ์ ํ์ฅํ๊ธฐ ์ํด์๋ WebConfigurer๋ฅผ ์์ ๋ฐ์์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ๋ฉด ๋ฉ๋๋ค.
(addArgumentResolvers, extendMessageConverters ๋ฑ ๊ฐ๋ฅ)
public interface WebMvcConfigurer {
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
}
๐ Reference