[스프링] JPA 상속관계 매핑

2025. 7. 25. 15:37·스프링

JPA를 사용하다 보면 하나의 부모 클래스(슈퍼타입)를 여러 자식 클래스(서브타입)가 상속받는 구조를 자주 만든다.
예를 들어, Item이라는 추상 클래스를 Book, Album, Movie가 상속받는 구조가 그 예다.

 

객체지향 프로그래밍에서는 이처럼 상속 구조를 활용하는 것이 자연스럽지만,
관계형 데이터베이스에는 상속이라는 개념이 존재하지 않는다.

 

대신 데이터베이스는 슈퍼타입/서브타입 모델링이라는 방식으로 이 문제를 해결한다.
그리고 JPA는 이 모델링 방식을 그대로 매핑할 수 있도록 다양한 전략을 제공한다.

 

그러므로 이번 장에서는 JPA의 상속관계 매핑에 대해 정리한다.
각 전략이 어떤 방식으로 테이블을 구성하며, 어떤 장단점을 갖는지, 실무에서는 어떤 전략이 자주 쓰이는지 중심으로 설명한다.

 

상속관계 매핑이란?

객체의 상속 구조를 데이터베이스의 테이블 구조로 변환하는 방식이다.
JPA에서는 세 가지 전략을 제공한다:

  1. JOINED 전략 (조인 전략)
  2. SINGLE_TABLE 전략 (단일 테이블 전략)
  3. TABLE_PER_CLASS 전략 (구현 클래스마다 테이블 생성)

이 전략들은 @Inheritance(strategy = ...) 애너테이션으로 설정한다.
그리고 구분 컬럼을 사용하기 위해 @DiscriminatorColumn, @DiscriminatorValue도 함께 사용된다.

 

공통 애너테이션 

@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "dtype")
@Inheritance 어떤 상속 매핑 전략을 사용할지 지정
@DiscriminatorColumn 테이블에 저장할 자식 타입 구분 컬럼 이름 지정
@DiscriminatorValue 각 자식 클래스가 어떤 값을 가질지 지정 (생략하면 클래스명)

 

 

1. JOINED 전략 – 정규화를 지향하는 방식

설명:
부모 클래스와 자식 클래스 각각 테이블을 만든 뒤, 조인으로 데이터를 합쳐 조회하는 방식이다.

예시 코드:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "dtype")
public abstract class Item { ... }

@Entity
public class Book extends Item {
    private String author;
}

 

ITEM 테이블

id                     name                                             price                                             dtype
1 JPA책 20000 BOOK
2 인셉션 15000 MOVIE

BOOK 테이블 (자식 전용 테이블)

id                        author                                                 isbn
1 김영한 978-1234567

 

장점

  • 테이블이 정규화되어 중복 데이터가 없다.
  • 외래 키 무결성 제약조건 설정 가능.
  • 저장 공간이 효율적이다.

단점

  • 조회할 때 매번 조인을 사용해야 하므로 성능이 떨어질 수 있다.
  • INSERT 시 부모 테이블 + 자식 테이블에 각각 저장해야 해서 SQL이 2번 실행된다.
  • 쿼리가 복잡해진다.

이 전략은 데이터가 크고 구조가 안정적이며, 정규화가 중요한 도메인에 적합하다.

 

2. SINGLE_TABLE 전략 – 단일 테이블에 몰아넣기

설명:
모든 자식 클래스의 데이터를 하나의 테이블에 통합해서 저장하는 방식이다.

예시 코드:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
public abstract class Item { ... }

 

 

장점

  • 조인이 필요 없으므로 조회 성능이 빠르다.
  • 쿼리도 단순하다.
  • INSERT도 테이블 하나에만 하면 되므로 효율적이다.

단점

  • 모든 자식 클래스의 필드를 하나의 테이블에 포함시켜야 하므로, 대부분의 컬럼이 null을 허용해야 한다.
  • 테이블이 커지고 컬럼이 많아져서 오히려 성능 저하가 생길 수 있다.

이 전략은 자식 클래스가 많지 않고, 공통 필드가 대부분인 경우에 적합하다. 성능 위주로 단순하게 구성하고 싶을 때 유용하다.

 

 

3. TABLE_PER_CLASS 전략 – 자식마다 독립 테이블

설명:
부모 클래스는 테이블 없이 매핑 정보만 제공하고, 각 자식 클래스가 자신의 테이블을 독립적으로 갖는다.

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item { ... }

 

장점

  • 서브타입을 명확하게 분리할 수 있다.
  • 자식 테이블에 NOT NULL 제약 조건을 자유롭게 걸 수 있다.

단점

  • 부모 타입으로 전체 조회하려면 UNION ALL이 필요하다 → 쿼리 성능이 매우 안 좋다.
  • 공통 필드를 반복해서 정의해야 하므로 비효율적이다.
  • JPA가 IDENTITY 전략과 호환되지 않는 등 제약이 많다.

이 전략은 거의 사용하지 않는다. JPA 팀도, DB 전문가도 추천하지 않는 전략이다.

 

@MappedSuperclass – 테이블 없이 공통 필드만 상속

@MappedSuperclass는 상속 매핑과는 다르다.
이건 진짜 상속 관계가 아니라, 단순히 여러 엔티티에 공통 필드를 제공하기 위한 상속 구조다.

 

  • 테이블과 매핑되지 않으며, 자식 클래스가 테이블 생성 시 이 필드들을 포함하게 된다.
  • 직접 조회하거나 저장하지 않는다. (em.find(BaseEntity.class)는 불가능)
  • @Entity 클래스만 상속받을 수 있다.
  • 주로 id, 생성일, 수정일, 작성자 같은 공통 필드를 위한 용도다。

예시 코드:

@MappedSuperclass
public abstract class BaseEntity {

    @Id
    @GeneratedValue
    private Long id;

    // 다른 공통 필드도 여기에 추가 가능
}
@Entity
public class Member extends BaseEntity {

    private String name;
}

 

이렇게 하면 Member 클래스는 별도로 @Id를 선언하지 않아도,
BaseEntity로부터 상속받은 id가 식별자(PK)로 사용된다.

 

마무리하며

상속관계 매핑은 처음 보면 복잡해 보이지만, 각 전략이 어떤 구조로 테이블을 만들고 어떤 식으로 쿼리가 나가는지만 이해하면 생각보다 단순하다.

 

JOINED는 정규화 지향, SINGLE_TABLE은 성능 지향, TABLE_PER_CLASS는 거의 안 씀.
그리고 공통 필드만 공유하고 싶을 땐 @MappedSuperclass로 처리하면 된다.

 

결국 중요한 건 지금 내가 어떤 구조를 원하고, 나중에 얼마나 바뀔 수 있느냐다.
잘못 선택하면 나중에 변경이 힘들 수도 있으니, 설계할 때 미리 전략을 잡고 가는 게 좋다.

 

감사합니다.

'스프링' 카테고리의 다른 글

[스프링] DTO가 필요한 이유  (3) 2025.07.27
[스프링] JPA에서 제공하는 쿼리 방법  (1) 2025.07.26
[스프링] JPA 연관관계 매핑  (2) 2025.07.25
[스프링] JPA 기본 매핑 어노테이션  (4) 2025.07.24
[스프링] JPA란?  (1) 2025.07.24
'스프링' 카테고리의 다른 글
  • [스프링] DTO가 필요한 이유
  • [스프링] JPA에서 제공하는 쿼리 방법
  • [스프링] JPA 연관관계 매핑
  • [스프링] JPA 기본 매핑 어노테이션
0kingki_
0kingki_
자바 + 스프링 웹 개발
  • 0kingki_
    0kingki_
    0kingki_
  • 전체
    오늘
    어제
    • 분류 전체보기 (134)
      • 코딩 테스트 (54)
      • 자바 (21)
      • 스프링 (27)
      • 타임리프 (16)
      • 스프링 데이터 JPA (8)
      • 최적화 (2)
      • QueryDSL (4)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    재귀
    타임리프
    스프링 컨테이너
    최적화
    LocalDateTime
    예외처리
    spring
    QueryDSL
    thymeleaf
    코딩테스트
    스프링 데이터 JPA
    자바
    fetch join
    JPA
    코딩 테스트
    Java
    쿼리dsl
    백준
    컬렉션
    쿼리
    예외 처리
    BFS
    다형성
    SOLID
    불변객체
    mvc
    SpringDataJpa
    스프링
    dfs
    객체지향
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
0kingki_
[스프링] JPA 상속관계 매핑
상단으로

티스토리툴바