1 분 소요

문제 상황

스프링 기반 API 서버를 구현 프로젝트에서 Controller 테스트를 하는 도중 아래와 같은 에러가 발생했다.

nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); 
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)

문제 원인

  • JPA 연관관계에서 양방향 매핑을 선언한 경우 발생
  • Jackson lib 의 ObjectMapper 객체에 의해 컨트롤러 단에서 JSON 타입을 변환하는 도중에 변환되는 엔티티의 필드가 다른 엔티티를 참조하고 그 엔티티 클래스의 필드가 또 다른 엔티티를 참조하고 … 무한루프

Diary를 단건 조회하는 과정에서 DiaryDetailResDto 안에 엔티티 자체를 응답 결과로 리턴하려다보니 발생한 에러였다.
엔티티 자체가 아닌, String 타입으로 리턴하니 해결되었다.

(두 엔티티가 양방향 연관 관계에 놓여있어 JSON으로 포맷팅하는 과정에서 무한 순환 참조가 되는 것이 주요 원인이었다.)

해결 방법

이 글이 글을 참고했다.

DiaryDetailResDto 를 다음과 같이 변경하였다. (✅ 표시를 확인하자!)

변경 전

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class DiaryDetailResDto {

    private String title;
    private String content;
    private Pet pet; // ✅
    private List<String> imgUrls = new ArrayList<>();
    private List<Stamp> stamps = new ArrayList<>(); // ✅
    private LocalDate createdAt;

    public DiaryDetailResDto(Diary entity) {
        this.title = entity.getTitle();
        this.content = entity.getContent();
        this.pet = entity.getPet(); // ✅

        for (DiaryImage image : entity.getImages()) {
            this.imgUrls.add(image.getImgUrl());
        }

        for (DiaryStamp diaryStamp : entity.getStamps()) {
            this.stamps.add(Stamp.getStamp(diaryStamp));
        } // ✅

        this.createdAt = entity.getCreatedAt();
    }
}

변경 후

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class DiaryDetailResDto {

    private String title;
    private String content;
    private String petName; // ✅
    private List<String> imgUrls = new ArrayList<>();
    private List<String> stampTypes = new ArrayList<>(); // ✅
    private LocalDate createdAt;

    public DiaryDetailResDto(Diary entity) {
        this.title = entity.getTitle();
        this.content = entity.getContent();
        this.petName = entity.getPet().getPetName(); // ✅

        for (DiaryImage image : entity.getImages()) {
            this.imgUrls.add(image.getImgUrl());
        }

        for (DiaryStamp diaryStamp : entity.getStamps()) {
            this.stampTypes.add(Stamp.getStamp(diaryStamp).getStampType().toString());
        } // ✅

        this.createdAt = entity.getCreatedAt();
    }
}



💛 개인 공부 기록용 블로그입니다. 👻

맨 위로 이동하기