
JSON 을 기반으로 전달된 HTTP Request Body 를 지정한 객체로 역직렬화를 하는 어노테이션이다. SpringBoot Starter 에는 JSON 을 Java 객체로 변환하거나, Java 객체를 JSON 으로 변환시키는 Jackson 라이브러리가 존재하며, @RequestBody 역시 Jackson 라이브러리를 통해, 주어진 HTTP Body 정보를 Java 객체로 변환한다.
그렇다면 Jackson 라이브러리는 어떤 원리로 JSON 을 Java 객체로 변화시키는 것이 가능한가?
HTTP Message Converter 는 HTTP Request Body 를 Java 객체로 변환하거나, Java 객체를 특정 형태로 변환하는 역할을 한다. 요청이 오는 경우, 지정된 Content-type 에 따라, 사용되는 직렬화 종류가 다르며, JSON 의 경우에는 MappingJackson2HttpMessageConverter 를 통해, 직렬화/역직렬화를 수행한다.

MappingJackson2HttpMessageConverter 에서는 내부적으로, ObjectMapper 를 사용하여 실질적인 역직렬화를 수행한다. 이때, ObjectMapper 는 기본 생성자를 통해 DTO 를 생성하고, Reflection 을 통해, 필드 정보를 확인하여 필요한 데이터를 할당하는 방식으로 자바 객체를 형성한다.
ObjectMapper 는 일반적으로, 기본 생성자로 자바 객체를 한다. 물론, 기본 생성자가 없는 경우에는 deserializeFromObjectUsingNonDefault 라는 메서드를 호출하여, 이에 대응하고자 하지만, 클래스 정보를 파악할 수 있는 특정 조건에 해당되지 않는 경우, 자바 객체를 매핑하는 것을 실패할 수 있으므로, @RequestBody 에 해당하는 DTO 는 일반적으로 기본 생성자가 있는 것이 좋다.
기본 생성자가 없어도, 자바 객체로의 매핑을 가능하게 하는 조건은 다음과 같다.
JDK 8 이후 버전이며, 빌드 환경이 Graddle 인 경우 : 명시적 생성자 만으로 역직렬화가 가능해지는 모듈이 추가 되었으며, 이를 시행하기 위해서는 컴파일 시 -parameters 옵션이 설정되어야 한다. 이 설정이 Graddle 에서는 자동으로 적용된다.
생성자를 알려주는 @JsonCreater 이나 @JsonProperty 를 사용하는 경우 : 기본 생성자 없이, 생성자가 여러개라도 생성자를 지정해줄 수 있다. 단, Jackson 라이브러리에 종속적인 코드가 되어, 라이브러리 변화에 영향을 받는다.
@ConstructorProperties 를 사용하는 경우 : @JsonCreator 와 유사한 기능을 가진 JavaAPI 이다. 일반적이지 않은 어노테이션이기에, 협업 시 팀원이 이해를 못할 수 있다.
기본 생성자는 없고 인자가 한개만 존재하는 생성자에 대해 역직렬화를 동작하게 해주는 조건(
Properties-based, Delegation) 이 있는데 결국 이해하지 못했다. 지금으로서는 이런 조건도 있구나 정도로만 넘어가자.
ObjectMapper 는 필드 정보를 파악하기 위해, Getter 혹은 Setter 를 활용한다. 만약 getValue() 라는 메서드가 있다면, get 을 지우고, 첫자를 소문자로 바꾸는 것으로 필드의 값을 파악하는 것이다. 이후에 필드에 데이터를 할당할 때에는 Reflection 을 이용하여 주입하기 때문에, Getter 만 있어도 의도한 자바 객체 매핑이 가능하기 때문에, Setter 가 있어야 할 의미가 사라진다.
더불어, Getter 의 경우에는 매핑 이후, 추가적으로 개발자가 값을 꺼내는 용도로 사용할 수 있기 때문에, 이왕이면 Setter 보다는 Getter 가 활용성이 더 크다.
추가적으로, @ResponseBody 의 경우에는 직렬화시 Getter 를 통한 생성 방식을 적용할 수 있다. 위의 조건을 모두 암기할 수 있는 자신은 없으므로, Request 혹은 Response 에 대한 DTO 를 생성할 때에는, 기본 생성자와 Getter 를 넣어주는 규칙을 설정하여 개발을 하는 것이 용이하다고 판단했다.
물론
@RequestBody역시Getter가 없더라도, 필드에 직접적인 접근을 통해JSON을 반환하는 것도 가능하긴 하다. 필수적인 요구사항은 아니지만 더 편한 환경에서의 협업을 위해,DTO에 대한 규칙을 이런식으로 아예 고정 설정하는게 좋지 않을 까 라는 생각을 했다.
참고
[Spring] @RequestBody에 기본생성자만 필요하고 Setter는 필요없는 이유 - 1
[Spring] @RequestBody에 기본생성자만 필요하고 Setter는 필요없는 이유 - 2
@RequestBody Object Mapping 원리
[Spring] Spring MVC - @RequestParam,@ModelAttribute, @RequestBody 차이에 대해
[Spring MVC] @RequestBody 동작 원리 [1] - Http Message Converter.md
Spring MVC - HandlerMapping의 동작방식 이해하기 1편