Spring-Boot

[Spring-Boot] no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator) 에러 해결 방법

뚜코맨 2024. 2. 7. 18:10

문제

 

컨트롤러에서 /search GET 요청이 들어오면 SearchBoadRequest 라는 DTO를 requestBody로 받아와 boardService에 searchBoard() 메서드에 request를 보내주고 요청을 처리하게 되어있다.

 

여기서 문제는 DTO 사용에서 문제가 생긴 것이다.

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of `board.dto.SearchBoardRequest` (no Creators, like default constructor, exist):
cannot deserialize from Object value (no delegate- or property-based Creator)

 

Cannot construct instance of '~~~~.board.dto.SearchBoardRequest ~~~~ 이런 오류가 나왔는데 DTO 객체를 한번 보자.

 

 

별 문제가 없어보이지 않는가..

@Getter도 있고 인자 받는 생성자도 적어두었다..

 

그치만 여기서 중요한 점은 "인자가 없는 기본 생성자" 가 없다는 것이다.

 

💡 해결 방법

@Getter, @NoArgsConstuctor를 적어주면 해결이 된다..

 

 

 

그럼 기본 생성자가 없으면 왜 에러를 내는 지 알아보자.

 

우선, HTTP 요청으로부터 JSON 형식의 데이터를 받을 때, RestController에서 @RequestBody를 통해 DTO로 바인딩을 해주는 역할을 ObjectMapper가 해준다.

더보기

ObjectMapper 
Jackson 라이브러리에서 제공하는 클래스로써, Java Object ↔ JSON 파싱
직렬화 (Serialize) : Java Object → JSON
역직렬화 (Deserialize) : JSON → Java Object

이때, JSON으로부터 받아온 데이터를 가지고 DTO로 변환할 때 DTO의 기본 생성자를 이용하여 껍데기를 먼저 만드는 것이다.

그 다음으로, JSON의 필드와 DTO의 필드를 매칭시켜야하는데 이때는 Java 객체의 getter 및 setter 메소드의 이름을 파싱해서 매칭시킨다고 한다.

getter와 setter 메소드는 get 또는 set 뒤에 필드명이 붙기 때문에 이렇게 필드명을 읽어와서 변수를 매칭하는 것이다.

마지막으로, 생성된 DTO 객체의 매칭된 필드에 값을 넣어주기만 하면되는데, 이때 값을 넣어주는 과정에서 Reflection이라는 API를 이용해서 값을 넣어준다.

더보기

Reflection
Runtime에 동적으로 특정 Class의 정보를 추출할 수 있는 프로그래밍 기법
구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 Java API

일반적으로 객체의 값을 넣어줄 때는 setter를 떠올리지만 Reflection을 이용하면 단순히 필드명만 알아도 해당 필드를 조작할 수 있기때문에 DTO에서 Setter가 필요 없는 이유이다.

 

❗getter가 필수인 것이 아니다. getter 또는 setter 둘 중 하나는 존재해야 ObjectMapper가 바인딩 시 필드 매칭이 가능하고 그 후로는 Reflection을 이용해서 값을 할당하기 때문에 둘 중 하나만 있어도 되지만 굳이 그 둘 중 setter를 쓸 이유가 없을 뿐이다.

 

이와 같은 이유로 DTO 뿐만 아니라 JPA를 사용할때 인자가 없는 기본 생성자가 없으면 에러가 발생한다.

그 이유도 Entity 생성에도 Reflection API가 사용되기 때문이다.

 

결론은 ObjectMapper가 JSON으로부터 받아온 데이터를 가지고 DTO로 변환할 때 DTO의 기본 생성자를 이용하여 껍데기를 먼저 만드는데, 해당 코드에 기본 생성자가 없으면 에러를 발생시키는 것이기 때문이다.