DB 설계를 하다 보면 나도 모르게 다대다 관계를 생각해서 ERD를 그리는 경우가 있다.
이때마다 다대다 관계는 하면 안 되지!라는 생각을 갖고 다시 중간에 entity를 추가하여 일대다, 다대일 관계로 이루어지도록 수정하는 일이 빈번했다.
오늘은 왜 '다대다 관계'를 지양해야 하는지에 대해서 짚고 넘어가 보겠다.
다대다 관계란?
다대다 관계는 두 엔티티가 서로 여러 개의 데이터를 참조할 수 있는 관계이다. 예를 들어, 학생(Student)과 수업(Course)의 관계를 생각해 보자.
- 한 학생은 여러 수업에 참여할 수 있습니다.
- 한 수업에는 여러 학생이 참여할 수 있습니다.
@Entity
public class Student {
@Id
@GeneratedValue
private Long id;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<Course> courses = new ArrayList<>();
}
@Entity
public class Course {
@Id
@GeneratedValue
private Long id;
@ManyToMany(mappedBy = "courses")
private List<Student> students = new ArrayList<>();
}
코드 상으로는 @ManyToMany 애너테이션을 사용하면 조인테이블이 만들 수 있고, 다대다 관계는 성립하게 된다.
그런데 왜 다대다 관계를 지양해야 할까?
첫 번째 이유 : 조인 테이블 활용 불가
다대다 관계는 JPA에서 내부적으로 조인 테이블을 생성하여 구현된다. 위 예제에서 student_course 테이블이 생성되지만, 이 테이블은 자바 코드 상에서 직접 관리되지 않는다.
하지만 실제 개발에서는 개발자들은 조인 테이블에 추가적인 정보를 저장하거나, 이를 직접 조회해야 하는 상황이 자주 발생한다. 예를 들어, 학생과 수업의 관계에 등록일, 점수와 같은 정보가 필요한 경우에 조인테이블을 사용해야 한다.
조인 테이블에 추가 정보를 저장하려면 위와 같은 다대다 설계로는 불가능하며, 결국 설계를 변경해야 하는 불상사가 발생한다.
이런 상황을 예방하기 위해서는 일대다, 다대일 관계로 수정해야 한다.
@Entity
public class Student {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "student")
private List<Enrollment> enrollments = new ArrayList<>();
}
@Entity
public class Course {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "course")
private List<Enrollment> enrollments = new ArrayList<>();
}
@Entity
public class Enrollment {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "student_id")
private Student student;
@ManyToOne
@JoinColumn(name = "course_id")
private Course course;
private LocalDate enrollmentDate; // 등록일
private int grade; // 점수
}
이처럼 Enrollment 엔티티를 추가하여 조인 테이블을 엔티티로 명시적으로 정의하면, 조인 테이블에 추가적인 정보를 저장하거나 조작할 수 있다.
두 번째 이유 : 성능 저하
다대다 관계에서는 조인이 연쇄적으로 발생하기에 대규모 데이터에서 성능 문제를 야기한다. 특히 조인된 테이블 간의 조건 검색이나 정렬이 필요할 경우, 복잡한 쿼리가 생성되어 데이터베이스 부하가 커진다. 중간 엔티티를 추가하여 관계를 일대다, 다대일로 수정 후 JPA의 @Query나 네이티브 SQL를 사용해 성능을 최적화할 수 있다.
세 번째 이유 : 객체 지향적이지 않다
다대다 관계는 RDBMS의 개념이지, 객체지향의 개념이 아니다. 객체 지향 설계에서 엔티티는 책임과 역할이 명확해야 한다. 하지만 다대다 관계는 중간 테이블(조인 테이블)을 코드 상에서 명시하지 않으므로, 객체 간의 관계를 명확히 표현할 수 없다. 중간 엔티티를 도입하면 각 엔티티의 역할과 책임이 명확해지고, 객체 지향적으로 설계할 수 있다.
결론적으로
- 다대다 관계는 초기에 설계가 간단해 보일 수 있지만, 실제 운영에서는 문제가 발생할 가능성이 크다.
- 조인 테이블을 명시적으로 정의하여 일대다-다대일로 풀어내는 방식이 개발 생산성과 유지보수성 모두에서 좋기 때문에 이러한 방식으로 풀어내야 한다.
'WEB study > WEB(Springboot)' 카테고리의 다른 글
Refresh Token 사용 이유와 Redis를 활용한 관리 방법: Blacklist, TTL, 보안 강화 (0) | 2024.11.07 |
---|---|
웹 쿠키와 세션 개념 및 활용 예시 (0) | 2022.03.05 |
Spring Boot와 MyBatis의 연동 - Maven 의존성 설정 (0) | 2022.03.04 |
Spring Boot와 MyBatis의 연동 - Data Access Layer의 개념 (0) | 2022.03.03 |
Spring Bean 개념과 의존성 주입 - Spring Service 계층, Spring Bean, 의존성 주입 (0) | 2022.02.23 |