@RequestBody는 왜 빈 생성자를 필요로 할까? (1편)

허진혁·2022년 12월 19일
0
post-thumbnail

@RequestBody를 사용하면 기본생성자가 필요한 이유를 총 두편에 걸쳐 고민해보려 합니다.

이번편에서는 http요청이 올 때 body에 json형태의 값을 스프링이 어떻게 처리하는지 보려고 합니다.

오늘도 다시한번 만난 에러

InvalidDefinitionException: Cannot construct instance of 클래스명 (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

스프링에서 @RequestBody 애노테이션이 붙은 데이터를 가져올 때 위와 같은 에러를 만난다.

답은 수많은 사람들의 블로그에 나타나있다.

빈 생성자를 만들어라.

왜 그럴까? 답을 차근차근 알아가보자.

Spring은 Http Message Body를 읽을 때, HttpMessageConverter를 사용한다. 오류가 나타났을 때 아래 쭉 읽다보면 나왔던 객체이다. 그리고 사용하는 메서드는 read()이다.

즉, 클라이언트로가 요청을 보내면 스프링은 여러 Converter중 요청 값을 읽을 수 있는 Converter을 찾고 read()메서드를 통해 값을 읽고 원하는 Object로 변환한다.

실행되었을 때 아래와 같이 Json 형태의 요청이 들어오면, 스프링에서 사용하는 Jackson2HttpMessageConverter가 Json의 형변환을 담당한다. Jackson2HttpMessageConverter의 read()메서드가 실행되면 ObjectMapper를 통해 값을 변환시키는 구조이다.

자바 코드를 분석해보자.

@RequestBody를 들어가서 보면

The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request

요청에 들어오는 body는 HttpMessageConverter를 거치고, HttpMessageConverter에서 요청 내용을 확인하는 것을 발견했다.

내부를 살펴보면 @RequestBody, @ResponseBody의 처리를 도와주는 RequestResponseBodyMethodProcessor가 존재하는 것을 볼 수 있다.

그렇다면 여러 convert중 요청에 맞는 convert는 어떻게 찾을까?
다음과 같은 경로를 통해 convert목록을 가져와 사용 가능한 convert인지 확인하는 로직을 볼 수 있다.

public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
   // ...
   @Nullable
	protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {...}
   // ...
}

Json 형태의 요청이 올 때, 스프링은 여러가지 HttpMessageConverter 중MappingJackson2HttpMessageConverter를 사용하는 것을 찾을 수 있다.

정리해보자면
1. 클라이언트가 HttpBody에 Json형태의 요청을 보내면
2. 스프링은 HttpMessageConverter 목록을 가져오고
3. MappingJackson2HttpMessageConverter 선택해서
4. read() 메서드를 호출하게 되고
5. ObjectMapper가 JSON -> Object로 변환시켜준다.

다음편은 ObjectMapper의 변환 과정을 살펴보겠습니다.

참고한 자료
https://klyhyeon.tistory.com/250
https://velog.io/@conatuseus/RequestBody%EC%97%90-%EC%99%9C-%EA%B8%B0%EB%B3%B8-%EC%83%9D%EC%84%B1%EC%9E%90%EB%8A%94-%ED%95%84%EC%9A%94%ED%95%98%EA%B3%A0-Setter%EB%8A%94-%ED%95%84%EC%9A%94-%EC%97%86%EC%9D%84%EA%B9%8C-3-idnrafiw

profile
Don't ever say it's over if I'm breathing

0개의 댓글