![[Spring] 양방향 순환참조 해결하기](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGPpx9%2FbtsahKHJVWF%2FwxZH7b0j9GHVwJ85GHm4Dk%2Fimg.png)
유저와 리뷰의 관계가 1:N인 관계에서 유저와 리뷰의 정보를 가져오기 위해 코드를 작성하였으나, 아래와 같이 순환 참조 문제가 발생하였습니다. 이러한 문제가 왜 발생하는지, 그리고 해결 방법에 대해 알아보겠습니다.
순환 참조 문제
순환 참조란, 참조하는 대상이 서로 물려 있어서 참조할 수 없게 되는 현상을 말합니다.
JPA 순환 참조는 양방향 관계에서 일어날 수 있습니다. 양방향으로 연결된 엔티티를 JSON 형태로 직렬화하는 과정에서, 서로의 정보를 계속 순환하며 참조하여 StackOverflowError를 발생시킵니다.
여기서 직렬화란, 객체의 내용을 바이트 단위로 변환하여 네트워크를 통해 송수신 할 수 있도록 만드는 것입니다.
다음 코드는 유저와 리뷰에 대한 코드입니다.
@Entity
@Getter
@RequiredArgsConstructor
@AllArgsConstructor
public class Member {
@Id @GeneratedValue
@Column(name = "memberId")
private Long id;
private String name;
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<Review> review = new ArrayList<>();
}
@Entity
@Getter
public class Review {
@Id @GeneratedValue
@Column(name = "reviewId")
private Long id;
private String content;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "memberId")
private Member member;
}
@RestController
@RequestMapping("/app/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
/**
* 특정 회원 리뷰 조회 API (프로필)
*/
@GetMapping("/member/{reviewId}")
public BaseResponse<Review> getReview(@PathVariable("reviewId") long reviewId){
Review review = userService.findReview(reviewId);
return new BaseResponse<>(review);
}
}
Review 엔티티를 JSON 형태로 직렬화하는 과정에서, Member 엔티티를 조회하고, Member엔티티를 조회하는 과정에서 다시 Review 엔티티를 조회하고, 다시 Member 조회, Review 조회를 반복하면서 StackOverflowError를 발생시킵니다.
해결방법
1. @JsonIgnore 사용
@JsonIgnore은 해당 프로퍼티는 JSON 데이터에 null로 들어가게 한다. 즉, 데이터에 아예 포함이 안되게 한다.
@Entity
@Getter
@RequiredArgsConstructor
@AllArgsConstructor
public class Member {
@Id @GeneratedValue
@Column(name = "memberId")
private Long id;
private String name;
@JsonIgnore //추가
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<MemberReview> review = new ArrayList<>();
}
2. @JsonManagedReference와 @JsonBackReference 사용
연관관계의 주인(Review)의 member 필드에 @JsonManagedReference를, 연관관계 주인 반대(Member)의 review 필드에 @JsonBackReference를 추가해 순환 참조를 방어할 수 있다.
@JsonBackReference
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<MemberReview> review = new ArrayList<>();
@JsonManagedReference
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "userId")
private Member member;
3. 엔티티 대신 DTO 사용(가장 추천)
DTO 객체를 만들어 필요한 데이터만 옮겨 담아 리턴하면 순환 참조 문제를 방지할 수 있다.
이에 대한 코드는 다음에 더 자세히 다뤄보겠습니다.
참고자료
[JPA] @JsonIgnore, @JsonManagedReference, @JsonBackReference
순환 참조 발생 JPA는 디폴트로 맵핑된 데이터에 대해 FetchType.LAZY(게으른 불러오기)를 사용하게 된다. 예를 들어, User라는 Entity와 Acoount라는 Entity가 서로 양방향 참조 (1 :N)를 하고 있다고 해보자.
living-only-today.tistory.com
'Spring' 카테고리의 다른 글
[Spring] spring.datasource.driver-class-name 에러 (0) | 2023.04.25 |
---|---|
[Spring] 지연 로딩과 조회 성능 최적화 (0) | 2023.04.25 |
[Spring] 즉시 로딩(Eager Loading)과 지연 로딩(Lazy Loading) 알아보기 (0) | 2023.03.19 |
[Spring] Cascade옵션 알아보기 (0) | 2023.03.15 |
[Spring] Bean Validation 알아보기 (1) | 2023.03.09 |
느리더라도 단단하게 성장하고자 합니다!
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!