JPA에서 순환참조 무한루프를 해결하는 방법

1 minute read

JPA에서의 순환참조

JPA는 ORM이기 때문에 RDB를 관리하는데 있어서 양방향 참조를 필요로 한다.
이러한 참조는 필수적인 것은 아니지만 Entity는 데이터의 구조를 보여주는 역할도 하기 때문에 명시적으로 표시해주는 것이 좋은데 이 과정에서 순환참조의 무한루프에 빠지는 경우가 있다.

public class Module {
  @Id
  private long id;

  @OneToMany(mappedBy="module", fetch=FetchType.LAZY)
  private List Users;
}

public class User {
  @Id
  private long id;

  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="module_id")
  @LazyToOne(value = LazyToOneOption.NO_PROXY)
  private Module module;
}

위와 같이 Entity를 구성하여 Module 클래스를 호출하였을 경우 LazyFetch를 적용하였다 하더라도 모든 Entity를 순회하게 되고 이 과정에서 User 클래스를 참조하게 된다.
그러면 User 클래스는 다시 Module 클래스를 참조하게 되어 무한루프에 빠지게 되는 것이다.
이를 해결하기 위한 방법으로는 크게 2가지가 있다.

@JsonIgnore

JsonIgnore는 해당 어노테이션이 달린 객체를 무시하도록 하는 기능을 한다.
따라서 이 어노테이션을 자식의 module 변수에 붙이면 해당 클래스를 조회할 때 해당 변수는 무시되어 순환참조의 무한루프를 해결할 수 있다.
문제는 Module 클래스에 의한 순환참조로 User 클래스를 호출한 것이 아닌 User 클래스를 직접 호출했을때도 JsonIgnore는 기능하여 해당 변수를 무시한다는 점이다.

@JsonManagedReference, @JsonBackReference

JsonIgnore의 경우 본래 순환참조를 막기 위한 기능이 아니다. 단지, 활용에 따라 가능하기 때문에 쓰였을 뿐. 하지만 @JsonManagedReference, @JsonBackReference는 본래부터 이 순환참조의 문제를 해결하기 위한 용도로 만들어진 기능이다.
부모 클래스인 Module 클래스에 @JsonManagedReference를 추가하고
자식 클래스인 User 클래스에 @JsonBackReference를 추가하면
부모 클래스를 통해 순환참조 된 자식 클래스가 다시 부모 클래스를 참조하는 것을 막을 수 있다.