[Spring Error] JSON 파싱 에러

thdtjdals__·2023년 12월 1일

Spring Error

목록 보기
2/3
post-thumbnail
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이 인스턴스를 생성하는 방법을 알고 있는지 확인해 보는 것이 중요할 것 같습니다.
다만 이렇게 수정하게되면 설정한 엔티티와 서비스, 컨트롤러 클래스를 모두 수정해야할 수 도있게 되니 객체 설정을 처음부터 고려해서 코드를 작성해야 좋을 것 같습니다.

사진 출처 : 구글 이미지

profile
내가 보려고 만든 공부, 개발 정리

0개의 댓글