SPRING&BOOT/JPA

중간 테이블을 사용하는 이유

jki09871 2024. 9. 2. 11:08
중간 테이블(또는 조인 테이블)은 데이터베이스 설계에서 다대다(Many-to-Many) 관계를 표현하기 위해 사용된다. 다대다 관계에서는 두 개의 테이블이 서로 여러 개의 레코드를 가질 수 있는데, 이를 직접적으로 표현하기 어렵기 때문에 중간 테이블을 사용하여 이러한 관계를 관리한다.

중간 테이블을 사용하는 이유

  1. 다대다 관계 표현: 두 테이블 사이의 다대다 관계를 적절하게 표현하기 위해 중간 테이블을 사용한다. 예를 들어, 학생과 수업이라는 두 테이블이 있을 때, 한 학생이 여러 수업에 등록될 수 있고, 하나의 수업에 여러 학생이 등록될 수 있다. 이를 다대다 관계라고 하며, 중간 테이블을 사용하여 이러한 관계를 효율적으로 관리할 수 있다.
  2. 데이터의 중복 방지: 중간 테이블을 사용하지 않으면, 각 테이블에 중복된 데이터를 저장해야 할 수 있다. 중간 테이블은 이러한 중복을 피하고, 데이터를 정규화하여 데이터베이스의 무결성을 유지할 수 있게 한다.
  3. 확장성: 중간 테이블을 사용하면 다대다 관계에 대한 추가적인 정보를 저장하기 쉽다. 예를 들어, 학생이 수업에 등록된 날짜나 상태 등을 저장할 수 있다. 이러한 확장성은 중간 테이블이 없을 때보다 훨씬 유연하다.

코드 예시

스프링 부트와 JPA를 사용하여 중간 테이블을 사용하는 예제를 살펴보자. 예시로 Student와 Course 엔티티 간의 다대다 관계를 중간 테이블 Enrollment를 통해 관리하는 코드를 작성해본다.

import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Enrollment> enrollments = new HashSet<>();

    // getters and setters
}

@Entity
public class Course {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    @OneToMany(mappedBy = "course", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Enrollment> enrollments = new HashSet<>();

    // getters and setters
}

@Entity
public class Enrollment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "student_id")
    private Student student;

    @ManyToOne
    @JoinColumn(name = "course_id")
    private Course course;

    private String enrollmentDate;

    // getters and setters
}

코드 설명

  1. Student와 Course 엔티티는 다대다 관계를 가지고 있으며, 이를 Enrollment라는 중간 테이블을 통해 관리한다.
  2. Enrollment 엔티티는 student_id와 course_id를 외래 키로 가지고 있으며, 이 두 필드가 각각 Student와 Course 엔티티를 참조한다.
  3. Enrollment 엔티티는 enrollmentDate와 같은 추가적인 필드를 가질 수 있어, 학생이 수업에 등록된 날짜를 기록할 수 있다.
  4. 각 엔티티는 Set<Enrollment> 필드를 통해 다대다 관계를 표현하며, mappedBy 속성을 사용하여 중간 테이블에서의 연관 관계를 지정한다.

중간 테이블을 사용함으로써 줄일 수 있는 중복 코드는 데이터베이스에서의 다대다 관계를 직접 관리할 때 발생할 수 있는 데이터 중복이다. 만약 중간 테이블 없이 다대다 관계를 표현하려고 하면, 각 테이블에서 다른 테이블의 정보를 중복으로 저장해야 할 수 있다. 이것이 중복 코드 및 데이터로 이어질 수 있으며, 데이터의 무결성을 해칠 수 있다.

예를 들어, Student와 Course의 다대다 관계를 중간 테이블 없이 관리하려고 하면, Student 테이블에 여러 개의 Course 컬럼을 두거나, Course 테이블에 여러 개의 Student 컬럼을 두는 비효율적인 방법을 사용해야 할 것이다. 이는 다음과 같은 문제를 초래할 수 있다:

  1. 데이터 중복: 한 학생이 여러 수업에 등록될 때, 동일한 학생의 정보가 여러 번 반복되어 저장될 수 있다. 예를 들어, Student 테이블에 Course1, Course2, Course3 등의 필드를 만들어 각각의 수업에 대한 정보를 저장해야 할 수 있다. 이는 데이터베이스 설계의 정규화 원칙에 위배된다.
  2. 유지보수의 어려움: 데이터베이스의 스키마가 복잡해지고, 새로운 수업이 추가될 때마다 새로운 컬럼을 추가해야 하거나, 각 테이블에 많은 중복된 데이터가 추가되어 유지보수가 어렵게 된다.
  3. 비효율적인 쿼리: 다대다 관계를 중간 테이블 없이 표현하면, 특정 학생이 어떤 수업에 등록되어 있는지, 특정 수업에 어떤 학생이 등록되어 있는지 등을 효율적으로 쿼리하기가 어려워진다. 이는 성능 문제로 이어질 수 있다.

중간 테이블을 사용한 장점

중간 테이블을 사용하면 위와 같은 문제를 해결할 수 있다:

  • 정규화된 데이터: 중간 테이블은 각 테이블의 데이터를 중복 없이 관리할 수 있게 해준다. 학생과 수업 간의 관계는 중간 테이블에만 저장되므로, 데이터가 중복되지 않는다.
  • 확장성: 새로운 관계(예: 다른 수업에 대한 등록)를 추가할 때, 테이블의 구조를 변경할 필요 없이 중간 테이블에 새로운 레코드만 추가하면 된다. 이는 스키마 변경 없이도 데이터를 쉽게 관리할 수 있음을 의미한다.
  • 효율적인 쿼리: 중간 테이블을 통해 다대다 관계를 효율적으로 쿼리할 수 있으며, 이를 통해 성능을 최적화할 수 있다.

따라서 중간 테이블을 사용하면 코드와 데이터베이스 구조에서 불필요한 중복을 피하고, 유지보수성과 성능을 개선할 수 있다.