본문 바로가기

Spring/Spring JPA

[JPA]@ManyToOne 필드에 @ToString.Exclude 붙이는 이유

@ManyToOne 필드에@ToString.Exclude를 붙이는 이유는 양방향 관계에서 발생할 수 있는 순환 참조(Circular Reference) 문제를 방지하기 위해서입니다.

 

1. 문제의 원인: 순환 참조

 

JPA의 양방향 매핑에서 부모-자식 관계를 가지는 두 엔티티는 서로를 참조하게 됩니다.
예를 들어, Parent는 Child 목록을 가지고 있고, Child는 다시 Parent를 참조하는 구조가 있을 수 있습니다.
이 상태에서 toString()을 호출하면, 한 엔티티의 toString()이 다른 엔티티의 toString()을 호출하는 무한 루프가 발생할 수 있습니다.

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent")
    private List<Child> children;
}

@Entity
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne
    private Parent parent;
}

위 엔티티에서 Parent와 Child가 서로를 참조합니다.

 

만약 toString() 메서드가 자동 생성되어 있으면:

 

1. Parent의 toString()은 children 리스트를 출력하려고 Child 객체들의 toString()을 호출.
2. Child의 toString()은 다시 parent 객체의 toString()을 호출.
3. 이 과정이 계속 반복되면서 StackOverflowError가 발생.

 

2. 해결 방법: @ToString.Exclude

Lombok의 @ToString.Exclude를 사용하여 특정 필드를 toString() 결과에서 제외하면, 순환 참조 문제를 방지할 수 있습니다.

 

수정된 예제

@Entity
@ToString
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent")
    private List<Child> children;
}

@Entity
@ToString
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne
    @ToString.Exclude
    private Parent parent; // 순환 참조 방지
}

이제 Child의 toString()에서 parent는 제외되므로 Parent와 Child 간 순환 참조가 발생하지 않습니다.

 

3. 사용 시 주의할 점

1. 양방향 관계의 필드에 주로 사용: 순환 참조 문제가 발생할 가능성이 있는 필드에만 적용.
2. 비즈니스 로직에 영향 없음: @ToString.Exclude는 단지 toString() 출력에 영향을 주며, 엔티티의 데이터베이스 매핑이나 로직에는 영향을 미치지 않습니다.

만약 Lombok을 사용하지 않는다면, 수동으로 toString()을 작성하여 문제를 방지할 수 있습니다.

 

4. 결론 

1. @ToString.Exclude는 순환 참조 문제를 간단하게 방지하는 데 유용합니다.
2. 특히, @ManyToOne과 같은 양방향 관계에서 사용하는 것이 일반적입니다.
3. 이런 조치를 통해 StackOverflowError 같은 예외 상황을 사전에 예방할 수 있습니다