-
JPA 일대일 양방향 매핑에서 발생한 무한 순환 참조 문제와 해결프로젝트, 트러블슈팅 2024. 12. 23. 15:51

발생한 문제
게시글(Post)과 게시글 내용(PostContent)을 분리해서 관리하기 위해 일대일 양방향 매핑을 사용했다. 그런데 이 과정에서 StackOverflowError가 발생했다.
문제가 된 코드는 다음과 같다:
@Entity public class PostContent { @OneToOne private Post post; public void setPost(Post post) { this.post = post; post.setPostContent(this); // 여기서 Post의 setPostContent 호출 } } @Entity public class Post { @OneToOne(mappedBy = "post") private PostContent postContent; public void setPostContent(PostContent postContent) { this.postContent = postContent; postContent.setPost(this); // 여기서 다시 PostContent의 setPost 호출 } }
무한 참조 순환은 왜 일어났을까?
위 코드에서 post.setPostContent()를 호출하면:
- Post의 setPostContent() 실행
- postContent 설정
- PostContent의 setPost() 호출
- PostContent의 setPost() 실행
- post 설정
- 다시 Post의 setPostContent() 호출
- 1번으로 돌아가서 무한 반복...
결국 이런 순환 호출로 인해 스택이 계속 쌓이다가 StackOverflowError가 발생한다.
실제 에러메세지는 이렇다.
java.lang.StackOverflowError at se.sowl.postHubingdomain.post.domain.Post.setPostContent() at se.sowl.postHubingdomain.post.domain.PostContent.setPost() at se.sowl.postHubingdomain.post.domain.Post.setPostContent() at se.sowl.postHubingdomain.post.domain.PostContent.setPost() ... (계속 반복)
해결 방법
나는 이 문제를 해결하기 위해 각 setter에 조건문을 추가했다:
@Entity public class PostContent { @OneToOne private Post post; public void setPost(Post post) { // 현재 PostContent 객체의 post 필드를 매개변수로 받은 post로 설정 this.post = post; // 양방향 관계 설정을 위한 체크: // 1. post가 null이 아니고 (NPE 방지) // 2. 매개변수로 받은 post의 postContent가 현재 객체가 아닐 때만 // (이미 연결되어 있지 않은 경우에만 설정) if (post != null && post.getPostContent() != this) { // post에도 현재 PostContent 객체를 설정 post.setPostContent(this); } } } @Entity public class Post { @OneToOne(mappedBy = "post") private PostContent postContent; public void setPostContent(PostContent postContent) { // 현재 Post 객체의 postContent 필드를 매개변수로 받은 postContent로 설정 this.postContent = postContent; // 양방향 관계 설정을 위한 체크: // 1. postContent가 null이 아니고 (NPE 방지) // 2. 매개변수로 받은 postContent의 post가 현재 객체가 아닐 때만 // (이미 연결되어 있지 않은 경우에만 설정) if (postContent != null && postContent.getPost() != this) { // postContent에도 현재 Post 객체를 설정 postContent.setPost(this); } } }여기서:
- post != null: null 체크로 NPE( NullPointerException )를 방지한다.
- post.getPostContent() != this: post의 content가 현재 content가 아닌 경우에만 새로 설정한다.
이렇게 하면 양방향 관계 설정이 한 번만 이루어지고 무한 루프가 발생하지 않는다.
배운 점
이번 무한 순환 참조 문제를 해결하면서 몇 가지 중요한 점을 배웠다.
첫째, 양방향 관계 설정은 신중하게 해야 한다. Post와 PostContent처럼 서로를 참조하는 관계에서는 한쪽이 수정되면 다른 쪽도 함께 수정되어야 하는데, 이 과정에서 무한 루프같은 문제가 발생할 수 있다. 그래서 양방향 관계를 맺을 때는 어떤 상황이 발생할 수 있는지 미리 잘 고려해야 한다.
둘째, 무한 순환 참조 문제는 조건문으로 해결할 수 있다. 나는 처음에 단순히 setter만 호출했다가 무한 루프에 빠졌다. 하지만 현재 객체가 이미 상대방과 연결되어 있는지 확인하는 조건문을 추가함으로써 이 문제를 해결할 수 있었다. post.getPostContent() != this와 같은 조건 체크로 불필요한 순환 호출을 방지할 수 있다는 걸 알게 됐다.
셋째, null 체크의 중요성을 깨달았다. Java에서 null인 객체의 메서드를 호출하면 NullPointerException이 발생한다. 그래서 post != null처럼 null 체크를 꼭 해줘야 안전한 코드가 된다는 것을 배웠다.
'프로젝트, 트러블슈팅' 카테고리의 다른 글
복합 유니크 제약조건으로 중복 데이터 막기 (0) 2026.03.01 JPA N+1 문제와 @EntityGraph로 해결하기 (0) 2026.03.01 JWT 인증에서 Access Token, Refresh Token, Redis가 필요한 이유 (0) 2026.03.01 해커톤에서 OAuth2 로그인이 안 됐던 이유 (0) 2026.03.01 SMTP 이메일 인증 시스템 (0) 2025.07.11 - Post의 setPostContent() 실행