이전 글에서는 RestController에서
@RequestBody
바인딩을 Jackson 라이브러리의 ObjectMapper가 하는 것을 확인했습니다.
그리고 RequestBody를 생성할 때, DTO가 Property기반이 아니거나 Delegate를 한 상태가 아니라면 기본 생성자로 생성한다는 것을 알아냈습니다.
이번 글에서는 기본 생성자로 생성하는데, 어떻게 해당 DTO에 Setter가 필요없지? 라는 의문점을 풀어보도록 하겠습니다.
저는 RequestBody로 들어오는 RequestDto에 위와 같이 @NoArgsConstructor
, Getter
어노테이션만 작성했습니다.
위와 한다면 Setter
가 없는데 스프링이 어떻게 DTO에 값을 넣어줄까 의문을 가졌습니다. 이 문제를 해결하기 위해 ObjectMapper에 대해 좀 더 알아볼 필요가 있었습니다.
이를 알아보기 위해 여기를 참고했습니다.
이를 해석해 보면, 다음과 같습니다.
기본적으로 Jackson은 JSON 필드의 이름을 Java 오브젝트의 getter 및 setter 메소드와 일치시켜 JSON 오브젝트의 필드를 Java 오브젝트의 필드에 매칭합니다. Jackson은 getter 및 setter 메소드 이름의 "get"및 "set"부분을 제거하고 나머지 이름의 첫 문자를 소문자로 변환합니다.
예를 들어 brand라는 JSON 필드는 getBrand () 및 setBrand ()라는 Java getter 및 setter 메소드와 일치합니다. engineNumber라는 JSON 필드는 getEngineNumber () 및 setEngineNumber ()라는 getter 및 setter와 일치합니다.
쉽게말해 동일한 이름의 변수를 직접 찾는 것이 아니라, Getter와 Setter를 통해 찾아서 매칭한다는 것입니다.
변수의 매칭은 getter와 setter를 하는 것을 알아냈습니다. 그럼 그렇게 매칭한 것을 어떻게 주입시키는지 알아볼 차례입니다.
왜냐하면 setter를 사용해서 주입시킬 것이라 생각했었기 때문입니다.
BeanDeserializer를 생성하기 위한 BeanDeserializerFactory
에서 해당 작업을 수행하는 것을 확인할 수 있었습니다.
위의 addBeanProps 메서드를 들어가보면 아래와 같이 Setter와 Getter로 Bean의 프로퍼티를 찾는 것을 알 수 있습니다. (사진에 안보이지만 아래에는 Getter로 찾는 부분이 있습니다.)
BeanDeserializer
에서 DTO에 주입하는 코드를 찾았습니다.
아래 코드에서 p는 JsonParser입니다. propertyName으로 이전에 저장했던 프로퍼티를 가져옵니다. 그리고 이제 JsonParser와 Bean을 deserializeAndSet
메서드로 넘겨 값을 저장해주는 것입니다.
이제 prop.deserializeAndSet
메서드로 들어가봅시다!
여기서 이유를 찾았습니다!! 🎉
_field
는 java.lang.reflect
패키지의 Field 자료형입니다. 결국 reflection을 사용해서 값을 주입해주므로 Setter
는 필요하지 않는 것입니다.
테스트에서는 reflection을 사용해 value값을 보여드리기 위해 dto의 변수들을 모두 public으로 변경했습니다.
Setter & Getter 모두 없는 경우 테스트
objectMapper가 바인딩하는데 DTO에 setter와 getter가 없으니 찾지 못해 오류가 납니다.
Setter만 있는 경우 테스트
Getter만 있는 경우 테스트
이 테스트들을 통해, Setter와 Getter 모두 없는 경우에는 ObjectMapper
가 바인딩하는데 오류가 생기며
Setter, Getter 둘 중 하나만 있으면 된다는 것을 알았습니다.
@RestController
에서 @RequestBody
의 바인딩은 Jackson라이브러리의 ObjectMapper
가 해준다.ObjectMapper
는 @RequestBody
가 Property로 구현되어 있거나 생성을 위임한 경우가 아니라면 기본 생성자로 생성한다.@NoArgsConstructor
가 필요한 이유)Setter
보다 프로덕션 코드에 사용되거나 테스트에서 필요한 Getter
를 넣는게 좋겠다는 것이 저의 의견입니다. (쉽게 말해, Setter 또는 Getter 둘 중 하나만 있으면 되는데 Getter를 넣자!)오타 또는 잘못된 내용이 있으면 댓글이나 메일 주시면 정말 감사하겠습니다.
참고한 자료
최고예요