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편