
WARN 10 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of com.example.SignServer.Dto.PollDto (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.example.SignServer.Dto.PollDto (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1)<EOL> at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 63] (through reference chain: com.example.SignServer.Dto.CommentDto["pollId"])]
백엔드 작업을 모두 마치고 프론트엔드와 병합 후 댓글 작성에 대한 댓글 데이터 테스트를 실행 도중 발생한 오류입니다.
분명 스프링부트에서 POSTMAN 클라이언트로 데이터 CRUD 테스트를 했을땐 별다른 경고나 오류 없이 데이터가 DB값에 문제없이 잘 들어갔으나 프론트 리액트 네이티브에서는 400 오류와 함께 배포한 클라우드타입 서버에서는 위와같은 오류가 발생하였습니다.
위 오류 메세지를 살펴보면 "HttpMessageNotReadableException"과 같은 예외 메세지를 볼 수 있는데 이 예외는 HTTP 메시지를 읽는 데 실패했음을 나타내는 예외입니다.
이 오류는 주로 클라이언트에서 서버로 보내는 JSON 형식의 데이터를 서버에서 Java 객체로 변환하려고 할 때 발생합니다.
또 오류 메시지에서 'Cannot construct instance of com.example.SignServer.Dto.PollDto'는 'PollDto' 클래스의 인스턴스를 생성할 수 없음을 나타냅니다. 이는 해당 클래스에 적절한 생성자가 없거나, Jackson 라이브러리가 이해할 수 없는 생성자가 사용되었음을 의미합니다.
그리고 'no int/Int-argument constructor/factory method to deserialize from Number value (1)'는 'PollDto' 클래스에 정수 또는 Integer 인자를 받는 생성자 또는 팩토리 메소드가 없어서 숫자 1을 이용해 인스턴스를 생성할 수 없다는 뜻입니다.
이 문제를 해결하려면, 'PollDto' 클래스에 정수 인자를 받는 생성자 또는 팩토리 메소드를 추가해야 합니다. 이렇게 하면, Jackson이 JSON 데이터에서 숫자 값을 읽어 'PollDto' 인스턴스를 생성할 수 있게 됩니다.
@ToString
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class PollDto {
private Long id;
private String createdBy;
private Instant createdAt;
@NotBlank
private String title;
@NotBlank
private String question;
@Enumerated(EnumType.STRING)
private Category category; // 카테고리 필드 추가
private String user;
@Valid
private List<Choice> choice;
private int likesCount = 0;
//likedUserNicknames를 안 받게 해서
public static PollDto entityToDto(Poll poll) {
// UserEntity 집합을 닉네임 집합으로 변환
return new PollDto(
poll.getId(),
poll.getCreatedBy(),
poll.getCreatedAt(),
poll.getTitle(),
poll.getQuestion(),
poll.getCategory(),
poll.getUser().getNickname(),
poll.getChoices(),
poll.getLikesCount()
);
}
public Poll dtoToEntity(UserEntity user) {
Poll poll = new Poll();
poll.setId(this.id);
poll.setCreatedBy(this.createdBy);
poll.setTitle(this.title);
poll.setQuestion(this.question);
poll.setCategory(this.category);
poll.setUser(user);
poll.setChoices(this.choice);
poll.setLikesCount(this.likesCount);
return poll;
}
public static PollDto toDto(Poll poll) {
PollDto pollDto = new PollDto();
pollDto.setId(poll.getId());
pollDto.setTitle(poll.getTitle());
pollDto.setQuestion(poll.getQuestion());
pollDto.setCreatedBy(poll.getCreatedBy());
pollDto.setCategory(poll.getCategory());
return pollDto;
}
}
하지만 PollDto 클래스에서는 별다른 문제는 없어보입니다.
다시 오류를 검토하고 찾아본 결과 poll 테이블과 comment 테이블이 외래키로 연결된 comment의 pollId 컬럼의 문제라는 것을 알 수 있었습니다.
@Data
@ToString
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class CommentDto {
private Long id;
private UserDto userId;
private String uid;
private String nickname;
private Long pollId;
@NotBlank(message = "댓글을 입력하세요")
private String content;
private CommentDto parentComment;
private List<CommentDto> childrenComment;
private int likes;
private byte[] mediaData;
private String mediaName;
private String mediaPath;
private String mediaUrl;
private String time;
private Boolean report;
private ReportReason reportReason;
public static CommentDto toDto(Comment comment) {
CommentDto commentDto = new CommentDto();
commentDto.setId(comment.getId());
commentDto.setUserId(UserDto.toDto(comment.getUserId()));
commentDto.setNickname(comment.getUserId().getNickname());
if (comment.getPollId() != null) {
commentDto.setPollId(comment.getPollId().getId());
}
commentDto.setContent(comment.getContent());
commentDto.setLikes(comment.getLikes());
commentDto.setMediaName(comment.getMediaName());
commentDto.setTime(comment.getTime().toString());
commentDto.setReport(comment.getReport());
commentDto.setReportReason(comment.getReportReason());
commentDto.setMediaPath(comment.getMediaPath());
commentDto.setMediaUrl(comment.getMediaUrl());
}

작성한 commentDto 클래스를 보니 PollDto pollId 쪽에서 정수 또는 Integer 인자를 받는 생성자 또는 팩토리 메소드가 없어서 JSON 파싱 문제가 생긴 것 같습니다.

이렇게 하면 코드를 수정하면 PollDto 클래스와 CommentDto에 대한 JSON 파싱 오류인 Jackson 라이브러리는 JSON 데이터를 읽어 PollDto 인스턴스를 CommentDto에 생성하는 데 문제를 없애 해결할 수 있었습니다.
이런 오류가 발생했을때는 클래스에 적절한 생성자가 있는지 그리고 Jackson이 인스턴스를 생성하는 방법을 알고 있는지 확인해 보는 것이 중요할 것 같습니다.
다만 이렇게 수정하게되면 설정한 엔티티와 서비스, 컨트롤러 클래스를 모두 수정해야할 수 도있게 되니 객체 설정을 처음부터 고려해서 코드를 작성해야 좋을 것 같습니다.
사진 출처 : 구글 이미지