양방향 엔티티의 재귀 호출 문제와 해결 방법

새로 개발 중인 기능에 필요한 데이터 구조를 양방향 엔티티로 설계하게 되었습니다.

 

그런데 이 과정에서 양방향 참조로 인해 재귀 호출 문제가 발생하였고,

이를 해결할 방법을 찾아보게 되었습니다.

 

예시 코드들은 업무와 무관하게 작성되었습니다.

 


문제 상황

CategoryProduct 엔티티는 양방향 연관관계를 가지고 있습니다.

 

예를 들어, CategoryProduct의 리스트를 가지고 있고, Product는 다시 Category를 참조하는 구조입니다.

 

코드로 나타내면 다음과 같습니다.

@Entity
@Table(name = "category")
public class Category {

    @Id
    private Long id;

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Product> products;
}

@Entity
@Table(name = "product")
public class Product {

    @Id
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", insertable = false, updatable = false)
    private Category category;
}

 

이런 구조에서 Category 엔티티를 프론트로 전달하려고 접근했을 때,

재귀 호출로 인해 JSON 직렬화 과정에서 무한 루프가 발생하며 StackOverflowError가 발생했습니다.

 


문제 해결

해당 방법들은 크게 2가지로 해결할 수 있습니다.

  1. @JsonBackReference@JsonManagedReference 사용하기
    • @JsonBackReference: 자식 엔티티에서 사용하며, 부모 엔티티로의 참조를 직렬화하지 않아 무한 루프를 방지합니다.
    • @JsonManagedReference: 부모 엔티티에서 사용하며, 직렬화 대상 엔티티를 지정합니다.
    @Entity
    @Table(name = "category")
    public class Category {
    
        @Id
        private Long id;
    
        @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
        @JsonManagedReference
        private List<Product> products;
    }
    
    @Entity
    @Table(name = "product")
    public class Product {
    
        @Id
        private Long id;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "category_id", insertable = false, updatable = false)
        @JsonBackReference
        private Category category;
    }

    이 설정을 통해
    Category 엔티티를 JSON으로 직렬화할 때 Product는 포함되지만, 다시 Category로의 참조는 직렬화되지 않아 무한 루프 문제가 해결됩니다.

  2. @JsonIgnore을 이용해서 직렬화 제외 필드 설정하기
    • @JsonIgnore를 사용하여 직렬화 제외를 설정할 수도 있습니다. 하지만 이 경우 필드가 null로 처리되기 때문에 필요한 상황에서 문제가 될 수 있습니다.
    • 참고: StackOverflow