@RequestBody
로 받아오는 Dto에서 바인딩되지 않는 오류가 발생하였다.
현재 Dto는 모두 @Data
처리가 되어있다.
하지만 왜 우리의 Dto에 바인딩이 되지 않을까?
일단 내가 알고있는 지식으로 저번 편에 이어
해당 블로그를 참고하여 궁금증을 풀었다.(하나하나 들어가서 직접 같이 찾아보았다.)
왜 기본 생성자가 필요한가
굉장히 코드 분석이 잘 되어있어서 원리에 대해 알아갔다.
property명은 setter, getter 혹은 public field를 보고 찾는다.
값 주입은 java.lang.reflect 패키지를 사용해 직접 주입시킨다.
그리고 해당 @NoargsConstructor
는
이 부분에서 사용이 된다.
기본 생성자가 없다면 이 부분을 통과하지못하고
에러를 출력하게 되는 것이다.
그래서 우리는 @RequestBody
에 매핑될 클래스에 기본 생성자를 꼭 적어주어야한다!
해당 옵션으로 설정하였을 경우
기본 생성자의 접근 제어를 PROTECTED로 설정해놓게 되면 무분별한 객체 생성에 대해 한번 더 체크할 수 있는 수단이 되기 때문입니다
추가로 이펙티브 자바에는 이런 문구가 있습니다.
Effective Java - Item 15의 내용에서 접근 제한자는 가능한 좁히는 것을 원칙
가능한 한 다른 곳에서 접근 불가능하도록 만들어라. 클라이언트에게 제공하는 API만 public 또는 protected로 하라.
그래서 무분별한 생성자 생성을 방지하기 위해 PROTECTED 레벨로 설정합니다.
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
private Long id;
private int age;
private String email;
}
언뜻보면 이렇게 활용이 가능할듯하다.
하지만 해당 User는 Error를 동반한다.
왜냐하면 @Builder
같은 경우 생성자가 없으면 직접 모든 파라미터의 생성자를 만들어주지만 만약 생성자가 존재하면 그 생성자를 이용한다.
그래서 아무것도 없는 생성자에 빌더가 적용이 되어서 사용이 불가하다.
public User build() {
/// 일치하는 생성자가 없다.
return new User(this.id, this.age, this.email);
}
총 2가지가 있다.
@Builder
가 사용할 것이다.@Builder
@AllargsConstructor
public class User {
private Long id;
private int age;
private String email;
}
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
private String name;
private Long age;
private String email;
@Builder
public User(String name, Long age, String email) {
this.id = name;
this.age = age;
this.email = email;
}
}
개발자를 편리하게 해주는 롬복 잘 알고 사용하면 좋을 듯하다.
또한 두가지를 합치면
결국 @ResquestBody
는 기본 생성자와 빌더를 사용하게 될 경우 1번 경우를 사용하게 된다.
그렇다면 코드는
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllargsConstructor
public class UserDto {
private Long id;
private int age;
private String email;
}
가 될 것이다.
추가로 해당 문제를 해결하면서
리플렉션과 기본생성자
문서도 함께 참고하였다.
https://blogshine.tistory.com/445
https://cobbybb.tistory.com/14
https://jaehoney.tistory.com/287