게시글 조회 중에 게시글과 댓글 전체 조회하는 과정에서 댓글이 자꾸 조회되지 않는 Stack Overflow error가 발생했다.
매핑해주는 관계에서 user와 comment사이에서 양방향 매핑이 되어서 사이클이 생성되며 무한루프에 빠지게 됐다.
java.lang.StackOverflowError
자바에서 발생하는 런타임 예외 중 하나로, 스택 메모리 공간이 꽉 차서 더 이상 메소드 호출을 처리할 수 없을 때 발생한다.
자바 프로그램은 메소드 호출을 처리하기 위해 스택 메모리를 사용하는데 메소드 호출이 발생하면 호출된 메소드의 정보가 스택에 쌓이게 된다. 그리고 해당 메소드의 실행이 완료되면 스택에서 제거된다. 그런데 메소드 호출이 지나치게 많거나 메소드 내부에서 무한 반복문이나 재귀 호출 등이 발생하면 스택 메모리 공간이 모두 소진되어 StackOverflowError 예외가 발생하게 된다.
스택(Stack)영역
: 메서드 호출과 관련된 임시 데이터를 저장하는 영역으로 임시적으로 사용되는 변수나 정보들이 저장되는 영역으로 참조형 주소값만 저장된다.
메서드 실행이 끝나면 해당 메서드와 함께 스택에서 제거되고 메서드 호출 시마다 새로운 스택 프레임(Stack Frame)이 생성되며, 로컬 변수, 매개변수, 리턴 값 등이 저장된다. 스택 메모리는 스레드마다 별도로 생성되며, 스레드 간에 공유되지 않는다.
해결방법1 : 양방향 관계에서 직렬화 방향을 설정해주어 순환참조를 해결할 수 있도록 설계된 어노테이션인 @JsonManagedReference 또는 @JsonBackReference를 사용한다.
역참조관계 : 객체들 간의 서로 참조하는 관계에서 어떤 객체가 자기 자신을 참조하거나, 다른 객체가 자신을 참조하는 것을 말하며 예를 들어, A 객체와 B 객체가 서로를 참조하는 상황에서, A 객체가 B 객체를 참조하고 있으면 B 객체는 A 객체를 역참조하고 있는 것.
직렬화 :객체를 직렬화하면 해당 객체를 파일로 저장하거나 네트워크를 통해 전송할 수 있다. 또한 직렬화된 객체를 역직렬화하여 다시 객체로 복원할 수 있다.
public class Parent {
private Long id;
private String name;
@JsonManagedReference
private List<Child> children;
}
public class Child {
private Long id;
private String name;
@JsonBackReference
private Parent parent;
}
위 코드에서 Parent 클래스는 Child 클래스를 매핑하는 children 필드를 가지고 있으며, Child 클래스는 parent 필드를 가지고 있다.
이 때 @JsonManagedReference 어노테이션은 Parent 객체를 직렬화할 때 참조하고 있는 Child 객체도 함께 직렬화하도록 지정한다. 반면 @JsonBackReference 어노테이션은 Child 객체를 직렬화할 때 parent 필드를 무시하도록 지정한다.
따라서, Parent 객체를 직렬화할 때는 Child 객체도 함께 직렬화된다. 그러나 Child 객체를 직렬화할 때는 parent 필드를 무시하므로, Parent 객체는 직렬화되지 않는다.
해결방법2 :@JsonIgnore 사용한다
해결방법3 : DTO를 사용한다
순환참조의 상황이 발생한 주 원인은 양방향 매핑이기도 하지만, 더 정확하게는 entity를 직접 반환한것이 순환참조 발생의 큰 원인중 하나이다.
entity 자체를 return 하는것이 아니라, DTO객체를 만들어 필요한 데이터만 반환하면 순환참조 관련 문제를 사전에 방지 할 수 있다.
📝 참조링크