Spring/Spring err

[Error] Jackson ObjectMapper : 멤버변수가 1개일 때

민돌v 2022. 11. 28. 01:12

 

 

📌 문제 상황

1. Spring 에서 Patch 로 Json body 를 @RequestBody Dto Class 로 받아오는 부분을 리팩토링

2. 기존 Dto Class 는 단 1개의 멤버변수를 가지고, @Getter @Setter 가 적용되어 있었음

3. 기존의 지식 - @Setter 없이 생성자만 있으면 jakson 에서 매핑이 된다는 걸로 인지하여 @Getter @Setter ➡️ @Value 로 수정

 

💥붐 ~~  json mapping error 발생 

 

error - code

DefaultHandlerExceptionResolver - Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.pood.server.facade.promotion.request.PromotionIdxRequest` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.pood.server.facade.promotion.request.PromotionIdxRequest` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (PushbackInputStream); line: 2, column: 5]]

 

Controller

@PatchMapping("/example")
public ResponseEntity<ResponseDto> patchMethod(@RequestBody RequestDto request) {
    return ResponseEntity.ok(service.event(request));
}

Dto

@Value
public class RequestDto {
    int idx;
}

 

📌 문제가 발생한 이유

 

1. Json to Dto 과정 시, Jackson 은 ObjectMapper 를 사용해 readValue( ) 메소드에서 기본생성자와 Setter를 사용해 DTO 클래스를 생성하고 값을 주입합니다.

2. objectMapper는 기본생성자 public getter 혹은 setter 혹은 public field를 보고 property명을 찾습니다. 그래서 기본생성자와 getter만 있어도 prorperty명을 찾아 값을 주입시켜줍니다. 

3. 그 후 값의 주입은 java.lang.reflect 패키지를 사용해 자바 리플렉터로 직접 주입을 시켜 setter가 없어도 주입이되는 과정을 거칩니다.

생성자의 유무를 판단하고 null 일때 기본생성자를 만들어주는 objectMapper의 내부 메서드

 

👏 단! Jackson 에서 이렇게 생성자를 생략할 수 있는 경우는 전제조건 이 있다고 합니다. ( 참고 - Jackson jdk8 module )

1. 표시되는 생성자가 여러 개 있고 기본 생성자가 없는 @JsonCreator경우 역직렬화를 위해 생성자를 선택해야 합니다.
2. jackson-databind보다 낮은 값 과 함께 사용하는 경우 2.6.0가 @JsonCreator필요합니다. 실제로 는 에서 해결될 생성자 검색 문제로 인해 @JsonCreator종종 필요합니다 .2.6.0+2.7
3. Person 클래스에 단일 인수 생성자가 있는 경우 해당 인수에 @JsonProperty("propertyName"). 이것은 레거시 동작을 보존하기 위한 것입니다. FasterXML/jackson-databind/#1498 을 참조하십시오.

즉 단일 인수인 경우, Jackson에서 인지를 하지 못하는게 아닐지.. 조심스러운 예측을 해봅니다 ㅎㅎ;;

 


 

📌 해결

따라서, 기본생성자를 만들어서 Jackson 이 인식할 수 있게? 해줘야하는거 같다는 가설을 세웠고, 테스트를 해보니 포스트맨에서 잘 호출이 되는걸 확인했습니다.

@Value 의 경우 인수가 immutable 하게 정의되기 때문에, 기본생성자 시에도 멤버변수에 값을 할당해주도록해야지만 합니다.

@Value
public class PromotionIdxRequest {

    @ApiModelProperty(value = "레코드 번호")
    Integer idx;

    public PromotionIdxRequest() {
        idx = null;
    }
}
@NoArgsConstructor
public class RequestDto {
    Integer idx;
}

 

 

 

 Json 이 Java Class 로 매핑되는 과정을 한번 정리해보면 좋을거같다~!!!

 

 

 

참고 : https://klyhyeon.tistory.com/250