쇼핑몰 개인프로젝트 구현 중 순환참조가 발생했다.
먼저 순환참조에 대해 알아보면,
순환 참조(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;
다시 테스트 해보면 정상적으로 응답하는 것을 확인할 수 있다.
'Spring > JPA' 카테고리의 다른 글
@Convert - T타입 + Map 사용하기 (0) | 2023.01.14 |
---|---|
JpaRepository 와 CrudRepository 의 차이점 (0) | 2022.12.04 |
data.sql 적용하기 (스프링부트 버전 2.7.x 이상 ) (0) | 2022.11.10 |
@OneToOne 는 1:1 을 보장하지않는다 + unique 제약조건 (0) | 2022.10.17 |
Where 다중 파라미터 사용 - 동적쿼리 (0) | 2022.10.12 |
댓글