데이터베이스에서 다대다(Many-to-Many) 관계는 두 엔티티가 서로 여러 개의 관계를 맺을 때 사용됩니다.
JPA에서는 이를 표현하기 위해 @ManyToMany를 기본적으로 지원하지만, 실무에서는 @OneToMany와 @ManyToOne 조합을 활용한 중간 테이블(Entity) 방식을 더 많이 사용합니다.
오늘은 그 이유와 해결법을 알아보겠습니다.
@ManyToMany는 두 테이블 간의 중간 테이블을 자동으로 생성하고 관리합니다.

Student ──────┐
| (자동 생성) student_class (중간 테이블)
Class ──────┘
1) Student 엔티티
@Entity
public class Student {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "student_class",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "class_id"))
private List<Class> classes = new ArrayList<>();
}
2) Class 엔티티
@Entity
public class Class {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToMany(mappedBy = "classes")
private List<Student> students = new ArrayList<>();
}
@ManyToMany는 중간 테이블을 자동으로 생성해 편리하지만 양방향 매핑되어있을 때 하이버네이트에 의해 생성된 중간 테이블은 관계 설정에 필수적으로 필요한 정보들만 담겨있을 뿐 비즈니스 로직상 필요한 정보들은 담기지 않습니다. 또한 불필요한 정보들도 자동으로 생성된 중간 테이블에 담길 수 있습니다. 따라서 @ManyToMany 보단 중간테이블에 대한 클래스를 직접 만들어 @ManyToOne과 @OneToMany의 조합을 만드는 것이 좋습니다.
실무에서는 중간 테이블을 엔티티로 직접 생성하여 @OneToMany와 @ManyToOne을 조합하는 방식을 권장합니다.
등록 날짜, 권한 등)를 자유롭게 넣을 수 있습니다.Student ── Enrollment ── Class

1) Student 엔티티
@Entity
public class Student {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "student", cascade = CascadeType.ALL)
private List<Enrollment> enrollments = new ArrayList<>();
}
2) Class 엔티티
@Entity
public class Class {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "clazz", cascade = CascadeType.ALL)
private List<Enrollment> enrollments = new ArrayList<>();
}
3) Enrollment (중간 엔티티)
@Entity
public class Enrollment {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "student_id")
private Student student;
@ManyToOne
@JoinColumn(name = "class_id")
private Class clazz;
private LocalDate enrollmentDate; // 수강 신청 날짜
private String status; // 수강 상태 (예: ACTIVE, COMPLETED)
}
1. 자동 생성 테이블 사용 여부
@ManyToMany를 사용해도 되지만, 비즈니스 로직이 추가될 가능성이 있는 경우 중간 엔티티를 만들어 사용하는 것이 좋습니다.fetch 전략을 잘 설정해야 합니다. (@ManyToMany는 기본적으로 Lazy로 설정됨)mappedBy를 명확히 설정해야 합니다.| 기법 | 특징 | 사용 시기 |
|---|---|---|
| @ManyToMany | 중간 테이블 자동 생성, 필드 추가 불가 | 단순한 다대다 관계일 때 |
| @OneToMany + @ManyToOne | 중간 엔티티에 비즈니스 로직 필드 추가 가능 | 비즈니스 필드 추가, 유연한 관리 필요한 복잡한 관계일때 |
@ManyToMany를 사용하면 안 되는 이유
@ManyToMany, @OneToMany, @ManyToOne관계 작성하기
[JPA] 다대다 N : N 관계 풀어내기 (중간 테이블 생성)