JPA 양방향 연관관계에서 순환참조 문제 해결하기

haruceki·2024년 9월 13일
0

Spring Boot에서 JPA가 REST api 구현시 json 형태로 메시지를 전달 하게 되는데, 이때 Object를 json으로 변환하기 위하여 Jackson 라이브러리를 이용한다. Jackson 라이브러리에서 Entity의 getter를 호출하고, 직렬화를 이용해 json으로 변환하게 전송가능한 형태로 바꾸어 준다. 이 과정 중 엔티티의 getter를 호출하는 과정에서 연관된 엔티티를 계속해서 불러오다 보면 순환참조가 발생하여 StackOverflowError 가 발생하게 된다.

간단하게 말하자면, JPA에서 연관관계에서 양방향 관계를 가질 때 엔티티 간의 서로를 참조하는 구조에서 직렬화/역직렬화 시 JSON 변환 과정에서 순환참조가 발생할 수 있다. 이는 적절히 처리하지 않으면 무한 루프가 발생하거나 성능 저하 또는메모리 문제를 초래할 수 있다.

1. 단방향 연관관계를 사용

필요하지 않다면 단방향 연관관계를 사용하는 것이 더 안전하다. 예를 들어, Parent는 Child를 참조하지만 Child가 Parent를 참조할 필요가 없는 경우, 단방향으로 설계하여 문제를 줄일 수 있다.

2. @JsonIgnore 사용

엔티티 클래스에서 JSON 변환시 해당 필드를 무시하도록 지정하는 방법으로, 간단하게 적용할 수 있는 방법이다. 하지만 이 방법은 해당 필드를 완전히 무시하게 되므로 필요한 경우 해당 필드를 포함하지 않아야할 경우에는 부적합하다.

public class Store {
    @JsonIgnore
    @OneToMany(mappedBy = "store", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Product> productList = new ArrayList<>();
}

3. @JsonManagedReference, @JsonBackReference 사용

@JsonManagedReference를 사용하여 부모 엔티티에서 자식 엔티티를 참조하고, @JsonBackReference를 사용하여 자식 엔티티에서 부모 엔티티를 참조한다.

(@JsonManagedReference는 역참조를 관리하는 객체에서 사용, @JsonBackReference는 역참조를 하지 않는 객체에서 사용)

이 방법은 자식 엔티티를 직렬화하지 않기 때문에 무한 순환 참조 문제를 방지할 수 있다.

public class Store {
    @OneToMany(mappedBy = "store")
    @JsonManagedReference
    private List<Product> productList;
}

public class Product {
    @ManyToOne
    @JsonBackReference
    private Store store;
}

4. DTO를 사용해 데이터 전송

엔티티 자체를 직렬화하거나 전달하면 연관관계의 순환참조가 발생할 수 있으므로, 데이터 전송용 객체(DTO)를 만들어 필요한 데이터만 담아 전송(dto를 직접 json으로 변환)하면 순환참조 문제뿐 아니라 보안 및 성능 측면에서도 이점을 제공하므로 가장 추천하는 방법이다.

Entity 클래스는 데이터베이스와 직접적으로 연관이 되어있는 핵심 클래스라, Entity 클래스를 Request/Response로 사용하는 것은 지양하고 컨트롤러에DB Layer 와 View Layer 의 역할 분리가 되는 구조가 좋기 때문이다.

public class StoreResponseDto {
    private Long storeId;
    private List<ProductResponseDto> productList;
    ...
}
profile
희망도 절망도 없이 매일 코딩을 한다.

0개의 댓글