기존에 필드 네이밍을 할 때 boolean 타입의 값은 isXXX 이라는 형태로 네이밍을 하는 습관이 있었고, 내부에 boolean 타입 필드에 isXXX 라는 변수명을 선언 하였고, 해당 객체를 리턴하자 접두어 is가 떨어진 변수와 isXXX변수가 존재하고 있었다.
문제 원인 파악 전 기존에 알고 있던 사실
기존에 @RequestParam, @ModelAttribute는 프로퍼티(setXXX)를 통해 바인딩을 한다고 알고 있었다.
@RequestBody는 httpMessageConverter를 통해 바인딩을 하기 때문에 setter가 없어도 가능하다.
테스트
단순히 받아온 값을 그대로 내려주는 API (boolean값을 is를 뗀 값으로 네이밍)
롬복의 @Data 어노테이션을 붙였을 경우
ModelAttribute를 사용한 경우 - 성공
RequestBody을 사용한 경우 - 성공
롬복의 @Data 어노테이션을 안붙인 경우
ModelAttribute를 사용한 경우 - 실패
RequestBody을 사용한 경우 - 성공
위의 테스트 결과로 도출한 결론은 기존에 알고 있었던 사실과 동일하게 RequestBody는 Property 없이도 객체 바인딩을 수행할 수 있다.(변수가 public이었기 때문에 동작할 수 있었다....)
그렇다면 boolean type 변수를 isFollowed를 사용하던 followed를 사용하던 상관없는 것이 아닌가?(...)
테스트
followed 변수를 isFollowed로 변경
롬복의 @Data 어노테이션을 붙였을 경우
ModelAttribute 사용
RequestBody 사용
위의 두 가지 경우 모두 followed라는 필드가 하나 더 생겨서 응답으로 리턴되었다
롬복의 @Data 어노테이션을 안붙인 경우
ModelAttribute 사용
위에서 봤듯이 어짜피 실패하는 케이스이다.
RequestBody 사용 - 원하는 대로 수행됨
followed라는 필드가 하나 더 생겨서 응답으로 리턴된 상황
테스트 결과로 @Data 어노테이션과 연관되어 있는 문제라고 의심!
Lombok이 만들어준 getter, setter이다.
boolean타입의 경우 변수 앞에 is를 붙이고 getter를 만든다.
현재 선언해준 isFollowed가 followed라고 선언된 것 처럼 프로퍼티가 생성되어 있다.
RequestBody는 HttpMessageConverter 로 인해 바인딩이 되는데 이때MappingJackson2HttpMessageConverter는 ObjectMapper를 사용하는데, 프로퍼티를 이용하기 때문에 getter / setter 둘 중 하나만 있으면 된다고 한다.
테스트를 진행하며 필드의 접근제한자에도 뭔가 관련이 있는 것 같다.
private로 테스트
필드가 is접두어를 뗀 값으로 나오고 항상 default값이 적용되어있음
public으로 테스트
필드가 하나 더 생성되어 전달된다.
isXXX
XXX
ObjectMapper에 대해서 살펴보자
ObjectMapper는 리플렉션을 활용해서 객체로부터 Json 형태의 문자열을 만들어내는데, 이것을 직렬화(Serialize)라고 한다. 해당 부분은 @ResponseBody나 @RestController 또는 ResponseEntity 등을 사용하는 경우에 처리된다.
ObjectMapper의 기본 설정으로는 public 필드 또는 public 형태의 getter(getX로 시작하는 메소드)만 접근이 가능하다.
이 기본 설정 때문에 public으로 두었을때 일반 필드에도 접근이 가능하여 두 개의 필드가 생성되었던 것이다.
결국은 ObjectMapper도 프로퍼티를 활용하여 역/직렬화를 수행한다.
정리
1차 테스트
접근제한자를 public으로 설정
@RequestBody
정상적으로 수행됨
Getter, Setter 둘다 없어도 필드를 통해 객체를 (역)직렬화 한다.
Getter, Setter가 있다면 프로퍼티, 필드 접근을 통해 (역)직렬화 한다.
이 때문에 public인 isXXX필드와 Lombok이 만들어준 isXXX 둘 다 접근이 가능하여 필드가 하나 더 생성되었던 것
@ModelAttribute
setter없이는 정상적인 수행 자체가 불가능
요청에 맞는 생성자가 있다면 setter없이도 정상적인 수행이 가능하다.
2차 테스트
접글 제한자를 private로 설정
@RequestBody
getter/setter 둘 다 없을 때 Could not find acceptable representation 에러 발생