[JPA]' java.lang.IllegalStateException: Cannot call sendError() after the response has been committed '

이용만·2022년 3월 16일
0

참고 : https://velog.io/@youns1121/JPA-java.lang.IllegalStateException-Cannot-call-sendError-after-the-response-has-been-committed

오류
' java.lang.IllegalStateException: Cannot call sendError() after the response has been committed '

2021-06-15 15:33:24.943 ERROR 17900 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.jpacrud.domain.Member["team"]->com.jpacrud.domain.Team["members"]->org.hibernate.collection.internal.PersistentBag[0]->com.jpacrud.domain.Member["team"]->com.jpacrud.domain.Team["members"]->org.hibernate.collection.internal.PersistentBag[0]->com.jpacrud.domain.Member["team"]->com.jpacrud.domain.Team["members"]->org.hibernate.collection.internal.PersistentBag[0]- with root cause

java.lang.StackOverflowError: null

원인
@GetMapping("/read") //조회, READ, Member
public Member read(@RequestParam Long memberId) {
Optional member = memberRepository.findById(memberId);

return member.get();

}

ManyToOne, OneToMany 양방향 관계에서 Member 엔티티를 조회할때
스프링에서 JSON변환을 담당하는 Jackson 라이브러리를 이용해 Entity 객체를 그대로 JSON 문자열으로 변환시키게 되는데 Member 객체의 team 필드가 Team 엔티티를 참조하고, Team 객체의 members 필드가 Member 엔티티를 참조 하고 이를 변환 시키는 과정에서 같은 데이터가 반복적으로 출력이 되는 무한 루프가 발생하는 순환 참조 문제

해결
Jackson Annotation Examples

@JsonManagedReference, @JsonBackReference 추가
@JsonManagedReference

양방향 관계에서 정방향(자식->부모) 참조할 변수에 어노테이션을 추가하면 직렬화에 포함된다
@JsonBackReference

양방향 관계에서 역방향(부모->자식) 참조로 어노테이션을 추가하면 직렬화에서 제외된다
Member

@ManyToOne
@JsonManagedReference // 순환참조 방지
@JoinColumn(name="team_id")
private Team team;

Team

@OneToMany(mappedBy = "team")
@JsonBackReference //순환참조 방지
private List members = new ArrayList<>();

DTO를 사용하여 반환

주 원인은 양방향 관계에서 entity 자체를 controller에서 그대로 return 하도록 설계한 것이 문제, entity가 변경될일이 있을 때 유연하게 대응하기 어렵기 때문에
DTO를 만들어 필요한 데이터만 옮겨담아 controller에서 return하면 순환참조 문제를 막을 수 있음

연관관계 재 설정
양방향 관계는 필수가 아닌 선택이다 비즈니스 로직에 따라 재설정 할 필요가 있다

profile
성장하는 개발자가 되고자 합니다.

0개의 댓글