본문 바로가기

Java/Spring

@JsonManagedReference와 @JsonBackReference

프로젝트 중 양방향 관계에 있어 순환참조가 반복됐다.

 

순환참조가 발생하여 결국 스택 오버 플로우에 빠지게 됐고 프로그램을 정상적으로 실행하지 못하게 된 상황.

 

이는 A가 B를 부르고 B가 A를 부르게 되면 나오는 상황이다. 끝없이 부르게 되니 스택 오버 플로우에 빠지게 된 셈.

이를 해결하기 위해 DTO에서 필요한 데이터만 응답하게끔 바꾸는 방법도 있지만, 스프링의 Jackson 라이브러리의 

@JsonManagedReference와 @JsonBackReference 어노테이션을 사용하여 위 상황을 방지할 수도 있다.

 

먼저 순환참조가 일어났을 때 에디터 및 포스트맨에서 어떤 반응이 나오는지 살펴보자.

 

게시글 엔티티와 댓글 엔티티를 만들어 게시글 1 : 댓글 N 관계로 맵핑하였다.

// 게시글 엔티티
@OneToMany(mappedBy = "article")
	private List<Comment> comments;
    
// 댓글 엔티티
@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn
	private Article article;

 

서로 다른 클래스임을 알아두자.(적어놨지만..)

두 관계를 명시한 후에 먼저 게시글부터 간단히 작성했다.

 

일단 게시글을 작성 성공.

 

게시글 작성은 잘 된다. 그 다음 해당 게시글 id를 맞추어 댓글을 작성해봤다.

 

...ㅋ

 

포스트맨 상황은 이렇게 나왔다. 이클립스에서는 어떤 반응이 나왔을까?

 

...ㅋ

무한히 서로 참조하여 결국 스택 오버 플로우에 빠져버렸다. 이를 방지하기 위해 위에서 언급했던 DTO에서 필요한 서로를 또 부르지 않게끔 가공하여 사용하는 방법도 있지만..

 

이번 글 제목처럼 @JsonManagedReference와 @JsonBackReference를 사용해보자.

// 게시글 엔티티
	@OneToMany(mappedBy = "article")
	@JsonManagedReference
	private List<Comment> comments;

// 댓글 엔티티
	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn
	@JsonBackReference
	private Article article;

 

먼저 연관관계 주인 반대의 엔티티쪽에  @JsonManagedReference를 선언해준다. 그 다음 연관관계의 주인, 즉 N 쪽에 @JsonBackReference를 선언한다. 설명에 앞서 실행부터 해보자.

 

ㅋ!

잘 됐다. 댓글을 여러 개 생성해보았다.

 

@JsonManagedReference와 @JsonBackReference는 양방향 관계에서 직렬화 방향을 설정하여 순환 참조를 막을 수 있도록 설계된 어노테이션이다. 즉 이 두 어노테이션은 순환 참조를 막기 위한 것.

 

@JsonManagedReference는 정상적으로 직렬화를 실행되고 @JsonBackReference은 직렬화가 되지 않도록 실행된다.

따라서 1 관계에 있는 쪽에서만 정상적으로 직렬화가 되기 때문에 1이 N를 부를 수 있게 되고, N 쪽에서는 직렬화가 막혔기 때문에 1을 부르지 않게 되어 순환 참조를 막을 수 있게 된다.

 

참고로 N 관계, 즉 연관 관계의 주인 쪽에서만 @JsonBackReference를 선언하고 반대쪽에선 @JsonManagedReference를 선언해주지 않아도 정상적으로 동작은 된다. 다만 서로의 관계를 확실하게 명시해주는게 중요할 것 같다.

 

// 게시글 엔티티
	@OneToMany(mappedBy = "article")
	private List<Comment> comments;
    
// 댓글 엔티티
	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn
	@JsonBackReference
	private Article article;

 

위 처럼 연관관계의 주인 쪽인 댓글(N관계)에서만 @JsonBackReference을 선언해주고 실행해보면

 

 

잘된다.