JPA 순환참조와 그 해결

Kim DongKyun·2022년 12월 25일
2

Weekly I Learned

목록 보기
7/8

개요

간단한 메모장 프로젝트를 만들면서 여러 오류를 경험했지만, 그 중에서도 기억에 남았던 순환참조 오류에 대해서 작성했다.

테이블간의 양방향 연관관계에서 순환참조가 발생할 수 있으므로, 아래와 같은 기초적 세팅을 먼저 보려고 한다.

  1. 양방향 연관관계에서 테이블은 지정한 FK를 통해 서로를 참조한다. 연관관계의 주인은 주인이 아닌 테이블에게 FK를 내주어 연관관계를 설정할 수 있도록 하며, 연관관계의 주인이 아닌 테이블은 mappedBy를 사용하여 주인이 아님을 밝힘과 동시에 연관관계의 주인이 어떤 테이블인지 알게한다.

위와 같이 Memo엔티티가 여러개의 Comment를 가지므로,

  • 커멘트 안에서는 @ManyToOne / @JoinColumn(name = "주인의테이블이름_ID")와 같은 형태로 관계를 설정한다.

  • 반대로 Memo안에서는 @OneToMany를 사용하며 mappedBy를 통해 comments가 연관관계의 주인이 아님을 알려준다.

순환참조

테이블간의 연관관계가 양방향이며, 서로가 각자의 엔티티를 반환하면 순환참조에 빠지게 된다.

위와 같이
Memo 엔티티는 Comments(List)를 가지며, 이 안에 있는 Comments엔티티는 Memo를 참조한다. Comments엔티티가 참조하는 Memo 엔티티는 Comments(List)를 가지며, 이 안에 있는 Comments엔티티는 Memo를 참조한다. 이 메모는 Comments를 가지며 이 Comments는 메모를 참조하고 이 메모를 Comments가....

컴퓨터는 터지게 된다.

그렇다면 어떻게 해결해야 깔끔하게 잘 해결했다고 소문이 날까?


해결방법 1. @JsonIgnore

위와 같은 형태로 했더니 순환참조가 사라졌다!!! 근데 왜 사라진거지?

  • 위 사진은 Comments 안에 정의된 Memo 필드 사진이다. 여기서 @JsonIgnore을 사용하면 Comments 안에 있는 Memo 필드 자체를 무시해버린다. 그러므로 Memo를 조회할 때 위와는 달리

Memo 안에 있는 comments를 불러오고, Comments는 Memo를 참조하는데 여기서 Memo는 @JsonIgnore 상태이므로 무시한다.

따라서 메모는 정상적으로 불려오고, Comments도 잘 나온다.

그런데 이렇게 해결하는 방법이 진짜 맞아? 이렇게 우악스럽게 해도 되는거야?


해결방법 2. Entity 그자체를 불러오지 말고 Dto로 반환하기.

문제가 되었던 것은 Memo엔티티를 조회할 때 이 엔티티 안에 있는 Comments가 참조되었던 것이다. 그렇다면 이를 List<엔티티>의 형태가 아니라 dto의 형태로 반환하면 어떨까?

위와 같이 리턴값을 바꾸자, 정상적으로 잘 작동했다.


배운 점

  • 엔티티를 직접 반환하면 안된다는 것을 배웠다. 직접 참조나 직접적으로 주는 것을 최대한 지양하기로 했다.

  • Dto는 컨트롤러와 서비스에서 쓰는 것을 원칙으로 하자는 교훈.

  • 데이터의 흐름은 역행해선 안된다. 즉, requestBody로 받아온 데이터가 Entity에 Dto 형태로 전달되어 테이블에 추가되는것은? ok. 그러나 반대의 경우 엔티티의 값이 그대로 Response된다? 절대안돼. 왜냐면 Service단에서 변환될 위험도 있고, 위와 같은 순환 참조의 위험도 있으며, 무엇보다 객체지향적으로 맞지 않아(Encaptulation)


    이번주 리뷰

    이번주는 개인 과제(메모프로그램 만들기)가 있어서 거기에만 계속 매달렸다. 여러 문제가 있었지만 위 순환참조 문제가 제일 기억에 남아서 기록으로 남겨본다.

    한주 평가 : 잘했다. 알고리즘도 게을러지지 않고 열심히 해서 뿌듯함.

1개의 댓글

comment-user-thumbnail
2023년 1월 9일

완전 필요한 내용이었는데 감사합니다!

답글 달기