3 minute read

무한 참조 에러

Staff 엔티티:

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

    @OneToMany(mappedBy = "staff", cascade = CascadeType.ALL)
    private List<TimeManagementSystem> timeManagementSystems;

    // Getter, Setter, Constructors, and other fields
}

TimeManagementSystem 엔티티:

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

    @ManyToOne
    private Staff staff;

    // Getter, Setter, Constructors, and other fields
}

기존 문제:

위의 Staff와 TimeManagementSystem 이 두 엔티티 사이의 관계에서 다음과 같은 문제가 발생했다.

에러 메세지

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
14:52:21.254 [restartedMain] ERROR o.s.boot.SpringApplication - Application run failed

문제점:

  1. Staff가 TimeManagementSystem을 참조하고 있고, TimeManagementSystem이 Staff를 참조하고 있으므로 서로 무한하게 참조한다.
  2. 이러한 참조가 무한하게 반복되면 스택 오버플로우 오류 또는 무한 루프로 이어질 수 있다.

해결법:

양방향 관계에서 어떤 엔티티가 주인이 되어야 하는지를 명시적으로 설정해야 한다.

Staff 엔티티 수정:

@Entity
public class Staff {
    //...

    @OneToMany(mappedBy = "staff", cascade = CascadeType.ALL)
    private List<TimeManagementSystem> timeManagementSystems;

    // Getter, Setter, Constructors, and other fields
}
  • @OneToMany 어노테이션의 mappedBy 속성을 사용하여 양방향 관계에서 역방향 관계를 설정한다.

TimeManagementSystem 엔티티 수정:

@Entity
public class TimeManagementSystem {
    //...

    @ManyToOne
    private Staff staff;

    // Getter, Setter, Constructors, and other fields
}
  • @ManyToOne 어노테이션을 사용하여 TimeManagementSystem 엔티티에서 Staff 엔티티를 참조하도록 한다.

위와 같이 수정하면 Staff가 TimeManagementSystem을 참조하고 있지만, TimeManagementSystem은 Staff를 참조하지 않으므로 양방향 무한 참조 문제가 해결된다.

이러한 수정은 JPA에서 양방향 관계를 설정할 때 필요하며, 주의하여야 한다. 엔티티 간의 관계를 명확하게 설정하면 양방향 참조로 인한 문제를 예방할 수 있다.

양방향 무한 참조의 주요 원인

양방향 무한 참조 문제의 주요 원인은 엔티티 간의 서로를 참조하는 양방향 관계 설정이다. 주로 @OneToMany@ManyToOne 어노테이션을 사용하여 관계를 설정할 때, 한 쪽에서는 다른 엔티티를 참조하고, 다른 쪽에서는 역참조를 하게 된다. 이때, 두 엔티티가 서로를 계속 참조하게 되면서 무한 루프가 발생할 수 있다.

따라서 양방향 관계를 설정할 때 주의해야 하며, 주인(Owner)과 양쪽(Inverse) 엔티티를 명확하게 구분하고, mappedBy 속성을 사용하여 양방향 관계의 주인을 설정해야 한다. 이런 방식으로 무한 참조 문제를 방지할 수 있다.

간단한 예로, @OneToMany@ManyToOne 관계에서 @ManyToOne 쪽에는 mappedBy 속성을 설정하여 주인을 명시적으로 지정하는 것이 도움이 된다. 이렇게 하면 양방향 관계에서 역참조가 없어지므로 무한 참조 문제를 예방할 수 있다.

어김없이 꼬리를 물어보자

양방향 무한 참조에 대해 다른 개발자 분과 대화를 하다보니, 비슷한 문제를 많이 겪으신 것 같아서 이야기를 나누는 도중에 스택 오버플로우 에러에 대해서도 이야기가 나왔다. 스택 오버 플로우와 양방향 무한참조의 증상은 서로를 무한하게 참조하거나, 호출이 무한하다는 나름의 공통점이 있는데 두개의 문제점에 대해서 비교해보고 알아보는 시간을 가져보자

스택 오버플로우(Stack Overflow)와 양방향 무한 참조(Endless Bidirectional Reference)는 다른 개념이며, 아래에서 각각을 비교해보겠다.

1. 스택 오버플로우 (Stack Overflow):

  • 스택 오버플로우는 주로 프로그램 실행 시 메모리 스택(Stack)이 너무 많은 데이터를 처리하려고 할 때 발생하는 오류이다.
  • 스택 메모리에는 함수 호출 및 로컬 변수 등이 저장되는데, 함수 호출 시 해당 함수의 정보가 스택에 쌓이고, 함수가 종료될 때 정보가 스택에서 제거된다.
  • 재귀 함수 호출에서 재귀 호출이 무한하게 일어날 경우 스택 메모리가 넘치면서 스택 오버플로우 오류가 발생한다.

예를 들어, 다음과 같은 재귀 함수에서 스택 오버플로우가 발생할 수 있다.

public void recursiveFunction() {
    recursiveFunction(); // 무한히 재귀 호출
}

2. 양방향 무한 참조 (Endless Bidirectional Reference):

  • 양방향 무한 참조는 주로 소프트웨어 개발에서 발생하는 디자인 이슈로, 두 개체 또는 엔티티가 서로를 참조할 때 발생한다.
  • 특히 데이터베이스와 JPA(Java Persistence API)와 관련이 있다. 예를 들어, 두 엔티티 클래스가 서로를 참조하는 양방향 관계 설정을 잘못하면 무한 참조가 발생할 수 있다.
  • 이러한 경우, 객체를 직렬화하거나 데이터베이스에 저장할 때 문제가 발생하며, 무한 루프로 인해 프로그램이 끝없이 실행될 수 있다..(나 처럼)

예를 들어, 다음과 같은 양방향 관계에서 무한 참조가 발생할 수 있습니다:

@Entity
public class Parent {
    @OneToMany(mappedBy = "parent")
    private List<Child> children;
}

@Entity
public class Child {
    @ManyToOne
    private Parent parent;
}
이 경우 Parent와 Child 엔티티는 서로를 계속 참조하게 되어 무한 참조가 발생하게 된다.

요약하면, 스택 오버플로우는 메모리 스택이 넘치는 오류로, 어떤 함수의 호출이 무한히 반복될 때 발생하게 되고,  반면 양방향 무한 참조는 두 엔티티나 객체가 서로를 참조할 때 발생하며, 주로 데이터베이스와 관련이 있습니다. 이러한 참조가 무한하게 반복될 때 객체 간의 상호 참조로 인한 문제가 발생하게 된다.  그 밖에도 여러가지 요인이 있다고 하는데 모두 겪어봐야 알 것 같다… !

Leave a comment