WHAT
프로젝트를 진행하던 도중 댓글이 작성된 게시글을 보는데 BeanPropertyWriter.java 에서 순환 참조로 인한 무한 루프가 발생했습니다.즉, 직렬화 해주는 과정에서 무한 루프가 발생합니다.
브레이크 포인트 찍고 돌리면 계속 여기서 돌고 돈다.
원인은 Dto에서 부터 시작한다.객체를 JSON으로 직렬화 해주는 과정에서 Comment라는 객체를 부른다. 직렬화 해야되서.
Comment 객체의 필드를 직렬화하려는데 필드에 BulletinBoard가 있다. BulletinBoard 필드에 접근하여 다시 직렬화하려는데 Comment가 있다. 이 과정이 반복되어 Comment > BulletinBoard > Comment 무한 루프에 빠지게 되면 StackOverFlow 예외가 터지게 됩니다. 이게 원인입니다.
HOW
구글링을 해보시면 여러가지 해결책을 볼 수 있습니다. 저는 직관적인 해결책 3 + 1가지만 가져와 보았습니다.
- @JsonIgnore
- @JsonProperty
- ResponseDto 분리
- 단방향으로 전환
1. @JsonIgnore
필드에 @Jsonlgnore를 달면 해당 필드의 직렬화/역직렬화를 제한합니다.
잘 모르겠다 싶으면 연관관계 양쪽에 @JsonIgnore 를 걸어봅시다. 물론 이로 인해 다른 문제가 발생할 수도 있습니다.
사용 예시
@Entity @Getter @NoArgsConstructor
public class BulletinBoard {
@OneToMany(mappedBy = "bulletinBoard", fetch = FetchType.EAGER)
@JsonIgnore
private List<Comment> comments = new ArrayList<>();
}
@Entity @Getter @NoArgsConstructor
public class Comment {
@ManyToOne
@JoinColumn(name = "BULLETIN_BOARD_ID")
@JsonIgnore
private BulletinBoard bulletinBoard;
조금 더 똑똑하 방법으로는 응답 객체를 잘 살펴보고 순환 참조의 시작점이 어딘지 찾아보면 됩니다.
순환 참조는 위 소스의 브레이킹 포인트부터 시작됩니다. 직렬화를 위해 comment 필드에 접근합니다. comment 필드는 Comment 타입입니다.
@Entity
public class Comment {
@ManyToOne
@JoinColumn(name = "BULLETIN_BOARD_ID")
// 여기에 @JsonIgnore
private BulletinBoard bulletinBoard;
이제 여기에 들어가서 하나하나 직렬화 과정을 거칩니다.
1. id 필드 직렬화
2. body 필드 직렬화
3. isDeleted 필드 직렬화
4. bulletinBoard 직렬화 이 과정에서 무한 루프가 시작됩니다. 직렬화를 위해 BulletinBoard 객체에 들어가게 되겠죠.
결국 이 문제를 해결하기 위해서는 bulletinBoard 필드 위에만 @JsonIgnore를 걸어주면 됩니다.
2. @JsonProperty
@JsonIgnore 주석에서는 Jackson 2.6 이상에서 @JsonProperty 사용을 권장하고 있습니다.
@JsonIgnore가 직렬화/역직렬화를 제한했다면 @JsonProperty는 이를 선택적으로 제한할 수 있습니다.
// Comment 엔티티 내부
@JsonProperty(access = WRITE_ONLY)
private BulletinBoard bulletinBoard;
3. 응답 Dto 분리
항해99에 함께하고 계신 혜민님이 주신 아이디어 였습니다. 감사합니다 :)
마지막입니다! 마지막으로 말하지면 브레이킹포인트가 무한 루프의 원인입니다.
따로 Board와 Comment를 분리해서 각각 직렬화하면됩니다.
시간이 늦었기에... 그리고 밀린 부채들이 너무나도 많기에 소스 코드는 추후에 올리도록 하겠습니다.
4. 단방향으로 전환
굳이 양방향이 필요없다면 단방향으로 전환해서 문제를 해결할 수 있습니다. 다만 소스 코드가 길어지겠죠 ㅎㅎ
'항해99' 카테고리의 다른 글
항해99 28일차 TIL1 - TIL 한 달 후기, 장단점 및 앞으로의 방향성 (0) | 2023.01.06 |
---|---|
항해99 27일차 TIL1 - JPA N:N 관계 풀어내기 (0) | 2023.01.05 |
항해99 4주차 WIL - IOC, DI (0) | 2023.01.02 |
항해99 24일차 TIL1 - 물리 삭제(hard delete)/논리 삭제(soft delete) (0) | 2022.12.31 |
항해99 23일차 TIL1 - 생성자를 직접 만드는 것과 Lombok을 사용해 만드는 것의 차이 (0) | 2022.12.30 |