T_era

상속매핑 전략과 선택 가이드 본문

Programing/Spring

상속매핑 전략과 선택 가이드

블스뜸 2025. 6. 10. 17:22

1. JOINED 전략

JOINED 전략은 부모 엔티티와 자식 엔티티를 각각 별도의 테이블로 매핑하고, 두 테이블을 조인하여 엔티티를 조회하는 방식이다.

구현 예시

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int price;
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {
    private String artist;
    private String etc;
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
    private String director;
    private String actor;
}

장점

  • 정규화된 테이블 구조: 데이터 중복이 없어 저장 공간이 효율적이다.
  • 외래 키 참조 무결성: 부모 테이블의 ID를 참조하여 데이터 일관성을 유지한다.
  • 데이터 일관성: 각 테이블이 자신의 데이터만 관리하므로 일관성이 높다.

단점

  • 조회 시 조인 발생: 엔티티 조회 시 여러 테이블을 조인해야 하므로 성능 저하 가능성이 있다.
  • 복잡한 쿼리: 여러 테이블 조인으로 인해 쿼리가 복잡해진다.
  • 저장 시 여러 테이블에 INSERT: 데이터를 저장할 때 여러 테이블에 INSERT 작업을 수행한다.

2. SINGLE_TABLE 전략

SINGLE_TABLE 전략은 부모 엔티티와 모든 자식 엔티티를 하나의 테이블에 매핑하는 방식이다. DTYPE과 같은 구분 컬럼을 사용하여 어떤 자식 엔티티인지 구분한다.

구현 예시

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int price;
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {
    private String artist;
    private String etc;
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
    private String director;
    private String actor;
}

장점

  • 조회 성능이 좋음: 조인 없이 단일 테이블에서 데이터를 조회하므로 성능이 뛰어나다.
  • 단순한 쿼리: 복잡한 조인이 필요 없어 쿼리가 단순하다.
  • 저장 시 단일 INSERT: 데이터를 저장할 때 하나의 테이블에만 INSERT 작업을 수행한다.

단점

  • 컬럼 수 증가: 모든 자식 엔티티의 컬럼이 하나의 테이블에 존재하여 컬럼 수가 많아진다.
  • NULL 값이 많음: 특정 자식 엔티티에서 사용하지 않는 컬럼에는 NULL 값이 저장된다.
  • 테이블 크기 증가: 모든 데이터가 하나의 테이블에 저장되므로 테이블 크기가 커진다.

3. TABLE_PER_CLASS 전략

TABLE_PER_CLASS 전략은 각 자식 엔티티를 별도의 테이블로 매핑하고, 부모 엔티티의 필드까지 각 자식 테이블에 포함하는 방식이다. 부모 엔티티 자체는 테이블로 생성되지 않는다.

구현 예시

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int price;
}

@Entity
public class Album extends Item {
    private String artist;
    private String etc;
}

@Entity
public class Movie extends Item {
    private String director;
    private String actor;
}

장점

  • 자식 엔티티별 독립적인 테이블: 각 테이블이 자신의 데이터만 포함하여 명확성을 높인다.
  • 조회 시 단일 테이블 접근: 특정 자식 엔티티 조회 시 조인이 불필요하여 빠르다.

단점

  • 부모 엔티티 조회 시 UNION: 부모 엔티티 타입으로 조회할 경우 모든 자식 테이블을 UNION으로 조회해야 하므로 성능이 저하될 수 있다.
  • 외래 키 참조 어려움: 부모 엔티티의 ID를 참조하기 어려워 외래 키 관계 설정이 복잡해진다.
  • 데이터 중복: 부모 엔티티의 공통 컬럼이 각 자식 테이블에 중복 저장된다.

4. 전략 선택 가이드

각 전략의 특성을 고려하여 프로젝트의 요구사항에 맞는 전략을 선택해야 한다.

  • JOINED 전략 선택 시: 정규화가 중요하고 조회 성능이 덜 중요한 경우에 적합하다.
    // 정규화가 중요하고 조회 성능이 덜 중요한 경우
    @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    public abstract class Payment {
        @Id
        @GeneratedValue
        private Long id;
        private int amount;
    }
    
    @Entity
    public class CardPayment extends Payment {
        private String cardNumber;
    }
    
  • SINGLE_TABLE 전략 선택 시: 조회 성능이 중요하고 자식 엔티티의 종류가 적은 경우에 적합하다.
    // 조회 성능이 중요하고 자식 엔티티가 적은 경우
    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    public abstract class Content {
        @Id
        @GeneratedValue
        private Long id;
        private String title;
    }
    
    @Entity
    public class Article extends Content {
        private String content;
    }
    
  • TABLE_PER_CLASS 전략 선택 시: 자식 엔티티별로 독립적인 테이블이 필요하고 부모 엔티티 조회가 적은 경우에 적합하다.
    // 자식 엔티티별로 독립적인 테이블이 필요한 경우
    @Entity
    @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
    public abstract class Document {
        @Id
        @GeneratedValue
        private Long id;
        private String name;
    }
    
    @Entity
    public class PDFDocument extends Document {
        private String pdfContent;
    }
    

5. 성능 비교

각 전략의 성능은 다음과 같이 비교된다.

  • 조회 성능: SINGLE_TABLE > TABLE_PER_CLASS > JOINED
    • SINGLE_TABLE: 단일 테이블 조회로 가장 빠르다.
    • TABLE_PER_CLASS: 특정 자식 엔티티 조회 시 빠르지만, 부모 엔티티 조회 시 UNION 발생으로 성능 저하가 있다.
    • JOINED: 항상 조인이 발생하여 조회 성능이 가장 낮다.
  • 저장 성능: SINGLE_TABLE > TABLE_PER_CLASS > JOINED
    • SINGLE_TABLE: 단일 INSERT로 가장 빠르다.
    • TABLE_PER_CLASS: 단일 INSERT로 효율적이다.
    • JOINED: 여러 테이블에 INSERT가 발생하여 성능이 저하된다.
  • 공간 효율성: JOINED > TABLE_PER_CLASS > SINGLE_TABLE
    • JOINED: 정규화된 구조로 데이터 중복이 없어 가장 효율적이다.
    • TABLE_PER_CLASS: 데이터 중복이 발생한다.
    • SINGLE_TABLE: NULL 값이 많아 비효율적일 수 있다.

6. 결론

JPA 상속 매핑 전략은 각기 다른 장단점을 가지고 있으므로, 프로젝트의 정규화 요구사항, 조회 및 저장 성능, 데이터 중복 허용 여부 등을 고려하여 최적의 전략을 선택해야 한다.