Spring message converter

강정우·2023년 12월 6일
0

Spring-boot

목록 보기
34/73

HTTP message converter

기본 동작구조

  • @ResponseBody 를 사용했을 때 귀찮게 getWriter().write() 하고 또 IOExeption을 추가하지 않아도 됐었다. 왜냐하면 HTTP의 BODY에 문자 내용을 직접 반환했기 때문이다.

  • 이는 viewResolver 대신에 HttpMessageConverter 가 동작한 것이고
    반환 타입이 String일 땐 기본 문자처리: StringHttpMessageConverter,
    reference 타입일 땐 기본 객체처리: MappingJackson2HttpMessageConverter가 동작한다.
    이땐 응답의 경우 클라이언트의 "HTTP Accept 해더"와 "서버의 컨트롤러 반환 타입 정보" 둘을 조합해서
    HttpMessageConverter 가 선택된다.
    또한 byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있다.

HTTP message converter를 사용하는 경우.
HTTP 요청: @RequestBody , HttpEntity(RequestEntity) ,
HTTP 응답: @ResponseBody , HttpEntity(ResponseEntity) ,

기능

  • HTTP 메시지 컨버터는 HTTP 요청, HTTP 응답 둘 다 사용된다.

  • canRead() , canWrite() : 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크

  • read() , write() : 메시지 컨버터를 통해서 메시지를 읽고 쓰는 기능

종류

0 = ByteArrayHttpMessageConverter
1 = StringHttpMessageConverter
2 = MappingJackson2HttpMessageConverter
  • 우선순위는 위와같이 가져간다.

ByteArrayHttpMessageConverter

  • byte[] 데이터를 처리한다.
  • 클래스 타입: byte[] , 미디어타입: / ,
  • 요청 예) @RequestBody byte[] data
  • 응답 예) @ResponseBody return byte[] 쓰기 미디어타입 application/octet-stream

StringHttpMessageConverter

  • String 문자로 데이터를 처리한다.
  • 클래스 타입: String , 미디어타입: /
  • 요청 예) @RequestBody String data
  • 응답 예) @ResponseBody return "ok" 쓰기 미디어타입 text/plain

MappingJackson2HttpMessageConverter

  • application/json
  • 클래스 타입: 객체 또는 HashMap , 미디어타입 application/json 관련
  • 요청 예) @RequestBody HelloData data
  • 응답 예) @ResponseBody return helloData 쓰기 미디어타입 application/json 관련

과정

HTTP 요청 데이터 읽기

  1. HTTP 요청이 오고, 컨트롤러에서 @RequestBody , HttpEntity 파라미터를 사용한다.

  2. 메시지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 위에 컨버터 종류를 쫙돌면서 canRead() 를 호출한다.

  • 대상 클래스 타입을 지원하는가.
    예) @RequestBody 의 대상 클래스 ( byte[] , String , HelloData )
  • HTTP 요청의 Content-Type 미디어 타입을 지원하는가.
    예) text/plain , application/json , /
  1. canRead() 조건을 만족하면 read() 를 호출해서 객체 생성하고, 반환한다.

HTTP 응답 데이터 생성

  1. 컨트롤러에서 @ResponseBody , HttpEntity 로 값이 반환된다.

  2. 메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 canWrite() 를 호출한다.

  • 대상 클래스 타입을 지원하는가.
    예) return의 대상 클래스 ( byte[] , String , HelloData )
  • HTTP 요청의 Accept 미디어 타입을 지원하는가.(더 정확히는 @RequestMapping 의 produces )
    예) text/plain , application/json , /
  1. canWrite() 조건을 만족하면 write() 를 호출해서 HTTP 응답 메시지 바디에 데이터를 생성한다.

예시

StringHttpMessageConverter 이 동작 되는 경우

content-type: application/json

@RequestMapping
void hello(@RequestBody String data){}

MappingJackson2HttpMessageConverter 이 동작 되는 경우

content-type: application/json

@RequestMapping
void hello(@RequestBody HelloData data){}

X 동작이 안 되는 경우

content-type: text/html

@ReqeustMapping
void hello(@RequestBody HelloData data){}

위치

  • 그럼 Http message Converter는 어디에 위치하고있을까?

  • 바로 애노테이션 기반의 컨트롤러, 즉 @RequestMapping 을 처리하는 핸들러 어댑터인
    RequestMappingHandlerAdapter (요청 매핑 헨들러 어뎁터)에 있다.

  • 다시 잘 생각해보면 위 사진에서보 볼 수 있듯, 각각의 컨트롤러에 HTTP req message가 들어오는데 이를 Steam 데이터 혹은 String 데이터 아님 json 데이터 혹은 @RequestParam 어노테이션으로도 간편하게 메시지를 받아왔다. 얘를 바로 convert 해주는 역할을 하는 것이다.

HandlerMethodArgumentResolver(AKA.ArgumentResolver)

  • 가만 보면 위 사진말고도 더 많은 파라미터를 처리할 수 있다.
    HttpServeltRequest, Model, @RequestParam, @ModelAttribute, @ReqeustBody, HttpEntity 등 같은 HTTP 메시지를 처리하는데 매우 유연하데 대부분의 경우의 수를 다 받을 수 있었다.
    이게 가능하게 해준것이 바로 ArgumentResolver이다.

  • 애노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdapter 는 바로 이 ArgumentResolver 를 호출해서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성한다.
    그리고 이렇게 파리미터의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다.

  • 참고로 얘가 처리할 수 있는 파라미터는 30개 넘는데 자세한건 Docs에서 찾아볼 수 있다.

즉, ArgumentResolver의 역할을 RequestMappingHandlerAdapter가 요청 파라미터를 보고 ArgumentResolver에 객체를 요청하면 갖다주는 역할을 한다.

동작과정

  • ArgumentResolver 의 "supportsParameter()" 를 호출해서 해당 파라미터를 지원하는지 체크하고, 지원하면 "resolveArgument()" 를 호출해서 실제 객체를 생성한다.
    그리고 이렇게 생성된 객체가 컨트롤러 호출시 넘어가는 것이다.

HandlerMethodReturnValueHandler(AKA.ReturnValueHandler)

  • 물론 req 말고도 response할 때도 굉장히 다양한 객체를 지원했다.

  • 이렇게 view를 반환할 땐 ModelAndView, String, 혹은 void로 아예 반환이 없기도 하고 혹은 또 @RestController나 @ResponseBody를 사용하여 Body를 반환할 수도 있었다.
    이 반환하는 역할을 해주는게 ReturnValueHandler이다.

  • 그래서 View를 반환할 때 void로 반환하거나 String으로 반환해도 알아서 찰떡같이 알아먹는게 얘가 다 알하서 해줬기 때문이다.

  • 얘도 마찬가지로 위의 공식문서에도 똑같이 확인할 수 있고 둘다 확장이 인터페이스로써 가능하다.

결국 둘다 Http message converter를 갖는다.

  • 결국 Http message converter는 둘다 사용하는데 모든 파라미터에 대해 Http message converter가 사용되는 것이 아닌, @ReqeustBody, @ResponseBody, HttpEntity 를 요청 혹은 응답할 때 사용된다.

  • 맥 단축키 cmd + [ : 이전 위치로 이동
    shift + opt + cmd + U : 상속관계 보기
    ctrl + alt + shift + u

  • 그래서 스프링 MVC는 @RequestBody @ResponseBody 가 있으면 RequestResponseBodyMethodProcessor() 를,
    HttpEntity 가 있으면 HttpEntityMethodProcessor() 를 사용한다.

확장에 열려있다.

  • 뭐 사실 확장할 일이 거의 없겠지만 참고로 확장이 가능하다는 것을 알아두면 좋다.

  • 참고로 기능 확장은 WebMvcConfigurer 를 상속 받아서 스프링 빈으로 등록하면 된다. (예제)

@Bean
public WebMvcConfigurer webMvcConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            //...
        }
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            //...
        }
     };
 }
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글