본문 바로가기
Spring/JPA

JPA 순환 참조 해결해보기

by YoonJong 2022. 11. 30.
728x90

쇼핑몰 개인프로젝트 구현 중 순환참조가 발생했다.

 

먼저 순환참조에 대해 알아보면,

순환 참조(Circular reference)란, 참조하는 대상이 서로 물려 있어서 참조할 수 없게 되는 현상을 말한다.

 

이전 JPA 강의를 들으면서 순환참조는 자주 발생한다 라고 해서 어떤건지 개념만 알고 있었는데,

처음 직접 마주치니 해결방법이 바로 떠오르지 않아 구글링을 통해 해결했다.

 

구현하고 싶었던 것은, 상품을 조회했을 때 goodsReponse 대로 응답을 내려주는 것인데 이 과정에서 순환 참조가 발생했다. 이 부분 중 imageList 와 options 부분에서 발생했다.

 

public class Goods extends BaseTimeEntity {

    ...
    @OneToMany(mappedBy = "goods", cascade = CascadeType.ALL)
    private List<Image> images = new ArrayList<>();

    @OneToMany(mappedBy = "goods", cascade = CascadeType.ALL)
    private List<Option> options = new ArrayList<>();
public class Image extends BaseTimeEntity {

    ...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "goods_id")
    private Goods goods;
public class Option extends BaseTimeEntity {

    ...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "goods_id")
    private Goods goods;

 

현재 이 상태에서 상품을 등록 후 상품을 조회해보면 아래와 같이 계속해서 참조 -> 참조 -> 참조 -> 참조 .. 무한대로 참조가 발생해 stackoverflow 가 발생한다.

 

스프링부트는 @ResponseBody 를 선언하면 Object 를 json 상태로 변환시키기 위해 Jackson 라이브러리를 이용하는데,

Jackson은 직렬화를 이용해서 json 형태로 객체를 변환시킨다.

 * 직렬화 : 객체를 바이트 단위로 변환해서 송수신 가능하도록 변환

 

현재 코드에서는 Goods 와 Images 는 1:N 관계이며, Goods 와 Options 도 1:N 관계로 매핑했다.

조회에 대한 흐름을 보면,

1. Goods 가 Images 와 Options를 직렬화한다.

2. Images 와 Options 에서 goods 를 직렬화한다.

3. 다시 Goods 가 Images 와 Options를 직렬화한다

..

이런과정이 반복된다.

 

해당 순환 참조를 해결하기 위해  @JsonManagedReference @JsonBackReference 를 사용했다.\

 

@JsonManagedReference mapped by 가 있는 곳에,

@JsonBackReference 는 연관관계의 주인에 적용시킨다.

 

public class Goods extends BaseTimeEntity {

   ...
    @JsonManagedReference
    @OneToMany(mappedBy = "goods", cascade = CascadeType.ALL)
    private List<Image> images = new ArrayList<>();

    @JsonManagedReference
    @OneToMany(mappedBy = "goods", cascade = CascadeType.ALL)
    private List<Option> options = new ArrayList<>();
public class Image extends BaseTimeEntity {

    ...
    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "goods_id")
    private Goods goods;
public class Option extends BaseTimeEntity {

   ...
    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "goods_id")
    private Goods goods;

 

다시 테스트 해보면 정상적으로 응답하는 것을 확인할 수 있다.

728x90

댓글