[Spring] @RequestBody - Spring의 JSON-Java Object 변환 원리

·2024년 2월 29일
0

Spring

목록 보기
5/6
post-thumbnail

@RequestBody

JSON 을 기반으로 전달된 HTTP Request Body 를 지정한 객체로 역직렬화를 하는 어노테이션이다. SpringBoot Starter 에는 JSONJava 객체로 변환하거나, Java 객체를 JSON 으로 변환시키는 Jackson 라이브러리가 존재하며, @RequestBody 역시 Jackson 라이브러리를 통해, 주어진 HTTP Body 정보를 Java 객체로 변환한다.

그렇다면 Jackson 라이브러리는 어떤 원리로 JSONJava 객체로 변화시키는 것이 가능한가?

HTTP Message Converter

HTTP Message ConverterHTTP Request BodyJava 객체로 변환하거나, Java 객체를 특정 형태로 변환하는 역할을 한다. 요청이 오는 경우, 지정된 Content-type 에 따라, 사용되는 직렬화 종류가 다르며, JSON 의 경우에는 MappingJackson2HttpMessageConverter 를 통해, 직렬화/역직렬화를 수행한다.

ObjectMapper가 자바 객체를 형성하는 방식

MappingJackson2HttpMessageConverter 에서는 내부적으로, ObjectMapper 를 사용하여 실질적인 역직렬화를 수행한다. 이때, ObjectMapper 는 기본 생성자를 통해 DTO 를 생성하고, Reflection 을 통해, 필드 정보를 확인하여 필요한 데이터를 할당하는 방식으로 자바 객체를 형성한다.

기본 생성자가 필요한 이유

ObjectMapper 는 일반적으로, 기본 생성자로 자바 객체를 한다. 물론, 기본 생성자가 없는 경우에는 deserializeFromObjectUsingNonDefault 라는 메서드를 호출하여, 이에 대응하고자 하지만, 클래스 정보를 파악할 수 있는 특정 조건에 해당되지 않는 경우, 자바 객체를 매핑하는 것을 실패할 수 있으므로, @RequestBody 에 해당하는 DTO 는 일반적으로 기본 생성자가 있는 것이 좋다.

기본 생성자가 없어도, 자바 객체로의 매핑을 가능하게 하는 조건은 다음과 같다.

  • JDK 8 이후 버전이며, 빌드 환경이 Graddle 인 경우 : 명시적 생성자 만으로 역직렬화가 가능해지는 모듈이 추가 되었으며, 이를 시행하기 위해서는 컴파일 시 -parameters 옵션이 설정되어야 한다. 이 설정이 Graddle 에서는 자동으로 적용된다.

  • 생성자를 알려주는 @JsonCreater 이나 @JsonProperty 를 사용하는 경우 : 기본 생성자 없이, 생성자가 여러개라도 생성자를 지정해줄 수 있다. 단, Jackson 라이브러리에 종속적인 코드가 되어, 라이브러리 변화에 영향을 받는다.

  • @ConstructorProperties 를 사용하는 경우 : @JsonCreator 와 유사한 기능을 가진 JavaAPI 이다. 일반적이지 않은 어노테이션이기에, 협업 시 팀원이 이해를 못할 수 있다.

기본 생성자는 없고 인자가 한개만 존재하는 생성자에 대해 역직렬화를 동작하게 해주는 조건(Properties-based, Delegation) 이 있는데 결국 이해하지 못했다. 지금으로서는 이런 조건도 있구나 정도로만 넘어가자.

Setter가 반드시 필요하지 않은 이유

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편

profile
새로운 것에 관심이 많고, 프로젝트 설계 및 최적화를 좋아합니다.

0개의 댓글