차근차근 JPA를 사용하여 로직을 만들고 잘 나오는지 테스트를 하고 있었다. 그런데 갑자기ㅠㅠ
java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
아주 난리가 났다..
쿼리를 살펴보니 계속해서 같은 쿼리가 반복되고 포스트맨으로 응답 결과를 보니 매핑관계에 따라 끝없이 타고 가는 것이였다 😭😭
'순환참조' 문제라 부르기도 하고 JPA를 사용하며 흔히들 겪는 문제라 한다.
즉 양방향 매핑된 경우 엔티티를 JSON으로 변환하는 도중(연관관계를 직렬화 하는 도중) 무한 참조로 인한 에러이다.
작성한 엔티티 객체를 살펴보면 Board객체에 작성자 정보로 Member객체를 참조하였고 Member객체에는 사용자가 작성한 게시판 정보로 Board객체를 참조하고 있다.
이런 경우 Board.Member ↔ Member.Board 이렇게 서로 순환참조하기 때문에 순환참조 문제가 발생한다.
추가로 순환 참조로 되어 있으면 같은 문제가 ToString을 호출할 때도 발생한다고 한다.
해결방법에는 여러 방법이 있다.
1. @JsonIgnore
엔티티 객체 내 양방향 매핑한 컬럼에 이 어노테이션을 붙여주면 해당 속성을 무시하게 된다.
(json 데이터에 해당 속성은 null로 들어가서 데이터에 아예 포함이 안됨)
하지만 이는 화면이나 API 로직이 엔티티에 범하는 모양이기 때문에 권장하지 않는다고 한다.
2. @JsonManagedReference & @JsonBackReference
순환참조를 막기위한 어노테이션으로 직렬화 방향을 정해준다.
부모 클래스(@ManyToOne)에는 @JsonManagedReference를, 자식 클래스(@OneToMany)에는 @JsonBackReference를 추가한다.
>> 나는 이 방법으로 문제를 해결하였다.
Member
public class Member extends DateEntity implements UserDetails {
...
@JsonBackReference
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<Board> boards = new ArrayList<>();
...
Board
public class Board extends DateEntity {
...
@JsonManagedReference
@ManyToOne
@JoinColumn(name = "member_no")
private Member member;
...
3. DTO로 변환
엔티티로 변환하지 않고 API당 별도의 DTO클래스를 만들어 엔티티를 DTO로 변환하고 변환한 DTO를 JSON으로 바꾸는 것이다.
API에서 데이터를 반환할 때 엔티티 자체가 아니라 API 스펙에 맞는 DTO를 만들어 변환하는 것을 권장한다.
4. @JsonIdentityInfo
순환참조될 대상의 식별키로 구분해 더이상 순환참조 되지 않게 한다.(Jackson 2.0 이상부터 가능)
인프런 질문 김영한님 답변 www.inflearn.com/questions/14559
[ERROR] JPAQueryFactory 의존성 문제 (0) | 2021.04.15 |
---|---|
[JPA] getOne과 findById ID로 객체 조회하기 (0) | 2021.04.14 |
[WEB] RESTful API 만들기 (0) | 2021.03.22 |
[JPA] 게시판 CRUD (0) | 2021.03.21 |
[SPRING SECURITY] 스프링 부트 환경설정 (0) | 2021.03.14 |
댓글 영역