인프런 커뮤니티 질문&답변

Hine님의 프로필 이미지
Hine

작성한 질문수

Spring JPA 다대다 관계 테이블 질문드립니다.

작성

·

676

·

수정됨

0

2개의 다대다 관계의 엔티티를 연결테이블을 이용해서 총 3개의 엔티티로 구성했습니다.

  1. Companion 엔티티

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@AllArgsConstructor
public class Companion {

    // Companion 엔티티는 동반 여행에 참여하는 개별 동반자의 정보를 나타냅니다.
    // 회원들과의 다대다 관계를 통해 여러 회원과 연결될 수 있으며, 동반 여행의 세부 정보를 저장합니다.

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // 동반자 엔티티의 고유 식별자

    private String name; // 동반자의 이름

    private String code; // 동반자를 구분하는 코드 정보

    private Boolean isStart; // 동반 여행의 시작 여부

    private Boolean isEnd; // 동반 여행의 종료 여부

    private String tendency; // 동반자의 특성 정보

    private String mate; // 동반자에 대한 추가 정보

    private LocalDateTime startTime; // 동반 여행의 시작 시간

    private LocalDateTime endTime; // 동반 여행의 종료 시간

    @OneToMany(mappedBy = "companion")
    private List<MemberCompanion> companionMembers = new ArrayList<>();
}
  1. Member 엔티티

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Member {

    // Member 엔티티는 시스템 사용자를 나타내며, 동반자와의 다대다 관계를 허용합니다.
    // 각 회원은 여러 개의 동반자와 연결될 수 있으며, 동반자 정보를 관리합니다.

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // 회원 엔티티의 고유 식별자

    @Column(unique = true)
    private String email; // 회원의 고유한 이메일 주소

    private String nickname; // 회원의 사용자명

    private String snsType; // 소셜 로그인 종류

    private String originalId; // 고유 아이디

    @OneToMany(mappedBy = "member")
    private List<MemberCompanion> myCompanions = new ArrayList<>();
}
  1. Companion과 Member를 연결하는 Entity

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class MemberCompanion {

    // MemberCompanion 엔티티는 회원과 동반자 간의 관계를 정의하는 엔티티입니다.
    // 회원과 동반자를 관련시키며, 회원-동반자 관계의 역할 정보를 포함합니다.

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // 회원-동반자 관계 엔티티의 고유 식별자

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member; // 회원

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companion_id")
    private Companion companion; // 그룹

    @Enumerated(value = EnumType.STRING)
    private Role role; // 회원-동반자 관계 역할 (예: 리더, 구성원)
}

 

문제가 발생한 것은, Companion을 build하고 생성된 Companion과 Member를 통해서 MemberCompanion을 생성한 후, 생성된 MemberCompanion을 다시 Companion의 companionMembers에 넣으려고하면 순환 참조 오류가 터집니다. Dto를 사용해봤지만 잘 해결이 안되어서 질문 올립니다.

/**
 * 그룹 등록을 처리하는 메서드
 *
 * @param companionAddRequestDto 그룹 등록 요청 DTO
 * @return ResponseEntity 객체를 반환하여 등록 성공 또는 실패 응답을 전송
 */
    @Transactional
    public ResponseEntity registCompanion(CompanionAddRequestDto companionAddRequestDto) {
        Member member = memberRepository.findById(companionAddRequestDto.getMemberId())
                .orElseThrow(() -> new EntityNotFoundException("회원을 찾을 수 없습니다."));

        Companion companion = Companion.builder()
                .name(companionAddRequestDto.getName())
                .code(generateRandomCode())
                .isStart(companionAddRequestDto.getIsStart())
                .isEnd(companionAddRequestDto.getIsEnd())
                .tendency(companionAddRequestDto.getTendency())
                .mate(companionAddRequestDto.getMate())
                .startTime(companionAddRequestDto.getStartTime())
                .endTime(companionAddRequestDto.getEndTime())
                .companionMembers(new ArrayList<>())
                .build();

        companionRepository.save(companion);

        // MemberCompanion 엔티티를 생성
        MemberCompanion memberCompanion = MemberCompanion.builder()
                .member(member)
                .companion(companion)
                .role(Role.LEADER)
                .build();

        memberCompanionRepository.save(memberCompanion);

        // 등록 성공 응답을 생성하고 반환
        return ResponseEntity.ok()
                .body(new SuccessResponseDto(true, "그룹 등록이 완료되었습니다.", companion));
    }

해당 코드에서

memberCompanionRepository.save(memberCompanion);

이후 companion.getCompanionMembers.add(memberCompanion)시 getCompanionMembers값이 null이라 나오고 다른 방법을 사용하면 순환참조가 발생합니다.

해결 방법이 있을까요..?

답변 1

0

안녕하세요 Hine 님, 인프런 AI 인턴이에요.

순환 참조 오류는 JPA의 양방향 관계 설정으로 인해 발생할 수 있습니다. 현재의 코드에서는 CompanionMember 엔티티의 다대다 관계를 MemberCompanion 엔티티를 중간에 두어 관리하고 있습니다. 이 경우 엔티티들 간의 관계 설정을 조금 수정해야 순환 참조 오류를 해결할 수 있습니다.

먼저, MemberCompanion 엔티티에서 Companion 엔티티와 Member 엔티티의 참조 관계를 mappedBy 옵션을 사용하여 연결하고 있습니다. 이 때, mappedBy는 연관 관계의 주인(Owner)을 설정하는 것으로, 여기서는 MemberCompanion 엔티티의 관계가 주인입니다.

따라서 MemberCompanion 엔티티에서 Companion 엔티티와 Member 엔티티의 참조 관계 설정을 수정해주세요. 예를 들어, MemberCompanion에서 Companion을 참조하는 필드명을 memberCompanion으로 변경하고 Member에서 MemberCompanion을 참조하는 필드명을 myCompanionMembers로 변경할 수 있습니다.

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class MemberCompanion {

    ...

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    private Member member; // 회원

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companion_id")
    private Companion memberCompanion; // 그룹

    ...
}
@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Member {

    ...

    @OneToMany(mappedBy = "member")
    private List<MemberCompanion> myCompanionMembers = new ArrayList<>();

    ...
}

이후, Companion 엔티티의 companionMembers 필드에서 mappedBy 옵션을 추가해야 합니다. 이렇게 하면 Companion 엔티티가 MemberCompanion 엔티티를 참조하게 됩니다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@AllArgsConstructor
public class Companion {

    ...

    @OneToMany(mappedBy = "memberCompanion")
    private List<MemberCompanion> companionMembers = new ArrayList<>();

    ...
}

이렇게 수정 후에는 순환 참조 오류가 발생하지 않도록 할 수 있습니다. 변경 후에는 관련된 필드들의 접근 방식과 데이터의 설정 등도 확인해보시기 바랍니다. 이렇게 수정한 뒤에는 해당 오류가 해결되었는지 확인해보세요. 문제가 지속된다면 추가적인 정보를 제공해주시면 더 자세한 도움을 드릴 수 있을 것입니다. 감사합니다.

Hine님의 프로필 이미지
Hine

작성한 질문수

질문하기