본문 바로가기

Spring&IntelliJ

@Inheritance에 대해서 (3가지 전략)

다음코드에서 InheritanceType.JOINED에 대해 알려줘.

 

자바 객체의 상속 구조를 JPA가 데이터베이스 테이블 구조로 어떻게 매핑할지 결정하는 어노테이션

입니다.

 

1. 실제로 테이블을 생성하는 역할은 누가 할까?

테이블 생성은 @Inheritance가 아니라 Hibernate의 DDL 자동 생성 기능이 담당합니다.

예를 들어 persistence.xml에서:

 
<property name="hibernate.hbm2ddl.auto" value="create"/>
 

또는 Spring Boot에서:

 
spring:
  jpa:
    hibernate:
      ddl-auto: create
 

이 설정이 있으면 Hibernate가:

  1. 엔티티 클래스를 분석하고
  2. JPA 어노테이션을 읽고
  3. SQL CREATE TABLE 문을 만들고
  4. DB에 실행합니다.

2. 그러면 @Inheritance는 무엇을 제공할까?

예를 들어:

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

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;
}
 

그리고:

 
@Entity
public class Book extends Item {

    private String author;
    private String isbn;
}
 

이 상태에서 Hibernate가 테이블을 만들 때:

@Inheritance(strategy = JOINED) 정보를 보고:

"아, Item과 Book은 상속 관계구나. JOINED 방식으로 테이블을 만들어야겠다."

라고 판단합니다.

그래서:

 
create table ITEM (
    item_id bigint not null,
    name varchar(255),
    price integer,
    primary key(item_id)
);
 

그리고:

 
create table BOOK (
    item_id bigint not null,
    author varchar(255),
    isbn varchar(255),
    primary key(item_id)
);
 

를 생성합니다.


3. 만약 @Inheritance가 없다면?

예를 들어:

 
@Entity
public class Item {
    @Id
    private Long id;
}


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

이렇게 하면 JPA는 기본 전략:

 
InheritanceType.SINGLE_TABLE
 

을 사용합니다.

즉:

ITEM
----------------
ID
NAME
PRICE
AUTHOR
 

처럼 하나의 테이블에 모든 것을 넣습니다.


4. 정리하면 역할 분담은 이렇게 됩니다.

역할담당
자바 클래스를 엔티티로 등록 @Entity
PK 지정 @Id
컬럼 이름 지정 @Column
상속 관계 매핑 방식 결정 @Inheritance
테이블 생성 SQL 실행 Hibernate + ddl-auto/create
DB에 실제 테이블 생성 DB(H2, MySQL, Oracle 등)

따라서 질문하신 내용을 조금 수정하면:

❌ "@Inheritance는 DB 테이블이 없는 상태에서 자바 코드를 보고 테이블을 생성해주는 어노테이션이다."

보다는

✅ "@Inheritance는 Hibernate가 자바의 상속 구조를 DB 테이블 구조로 변환할 때 어떤 방식으로 설계할지 알려주는 어노테이션이고, 실제 테이블 생성은 Hibernate의 DDL 자동 생성 기능이 담당한다."

라고 이해하는 것이 정확합니다.

 

1. 현재 코드의 의미

 
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item {
    
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}
 

이 코드에서 Item은 부모 클래스입니다.

예를 들어 아래와 같은 상속 구조를 생각해보겠습니다.

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


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


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

객체 관점에서는:

        Item
         |
 ----------------
 |       |       |
Book   Album   Movie
 

입니다.


JOINED 전략을 사용하면 테이블은 어떻게 만들어질까?

DB에는 다음처럼 만들어집니다.

ITEM 테이블

ITEM_IDNAMEPRICE
1 JPA책 30000
2 아이유앨범 15000

BOOK 테이블

ITEM_IDAUTHORISBN
1 김영한 1234

ALBUM 테이블

ITEM_IDARTIST
2 아이유

즉 부모 데이터는 부모 테이블에 저장하고,

자식만 가지고 있는 데이터는 자식 테이블에 저장합니다.


2. 조회할 때는 JOIN 발생

예를 들어:

 
Book book = em.find(Book.class, 1L);
 

하면 Hibernate는 내부적으로:

 
select
    i.item_id,
    i.name,
    i.price,
    b.author,
    b.isbn
from Item i
join Book b
    on i.item_id = b.item_id
where i.item_id = 1;
 

처럼 JOIN SQL을 만듭니다.

그래서 이름이:

JOINED
 

입니다.


다른 전략과 비교

JPA에는 대표적으로 3가지 상속 전략이 있습니다.


1) JOINED (현재 방식)

Item
 |
 +-- Book
 |
 +-- Album
 |
 +-- Movie
 

DB:

ITEM
 |
 +---- BOOK
 |
 +---- ALBUM
 |
 +---- MOVIE
 

장점:

✅ 정규화된 구조
✅ 중복 데이터 없음
✅ 데이터 무결성이 좋음

단점:

❌ 조회할 때 JOIN이 많이 발생
❌ 테이블이 여러 개라 복잡

실무에서 가장 정석적인 방식입니다.


2) SINGLE_TABLE

 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 

하나의 테이블에 모두 저장합니다.

DB:

IDNAMEPRICEAUTHORARTISTDIRECTORDTYPE
1 JPA책 30000 김영한 null null BOOK
2 앨범 15000 null 아이유 null ALBUM

테이블:

ITEM
 

하나뿐입니다.

장점:

✅ 조회 빠름 (JOIN 없음)

단점:

❌ 사용하지 않는 컬럼이 많음

예:

Album인데

AUTHOR
ISBN
DIRECTOR
 

컬럼이 필요 없습니다.


3) TABLE_PER_CLASS

 
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
 

각 자식 테이블에 부모 필드까지 모두 넣습니다.

BOOK:

IDNAMEPRICEAUTHOR
1 JPA책 30000 김영한

ALBUM:

IDNAMEPRICEARTIST
2 앨범 15000 아이유

ITEM 테이블은 없습니다.

장점:

✅ 조회할 때 JOIN 없음

단점:

❌ 데이터 중복 발생
❌ 부모 타입 조회가 어려움


실무에서는 무엇을 많이 사용할까?

대부분:

@Inheritance(strategy = InheritanceType.JOINED)
 

또는

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 

둘 중 하나를 선택합니다.

특히 JPA 강의(김영한님의 "실전! 스프링 부트와 JPA 활용")에서는 JOINED 전략을 기본 예제로 사용합니다.


추가로 현재 코드에는 작은 오류가 있습니다.

 
@Id
@GeneratedValue
@JoinColumn(name="item_id")
private Long id;
 

여기서:

 
@JoinColumn
 

은 사용할 수 없습니다.

@JoinColumn은 연관관계 매핑용입니다.

예:

 
@ManyToOne
@JoinColumn(name="member_id")
private Member member;
 

같은 곳에서 사용합니다.

ID 컬럼은:

 
@Id
@GeneratedValue
@Column(name="item_id")
private Long id;
 

가 맞습니다.


정리하면:

전략테이블 구조특징
JOINED 부모/자식 테이블 분리 정규화, JOIN 발생
SINGLE_TABLE 하나의 테이블 빠름, 컬럼 낭비
TABLE_PER_CLASS 자식별 테이블 중복 발생

현재 코드의 InheritanceType.JOINED는 객체의 상속 구조를 DB의 여러 테이블로 나누어 저장하고, 부모 PK를 이용해 JOIN하는 가장 객체지향적인 매핑 방식이라고 이해하면 됩니다.

 

TABLE_PER_CLASS전략에서 데이터 중복이 발생하는 이유가 뭐야?

InheritanceType.TABLE_PER_CLASS 전략에서 데이터 중복이 발생하는 이유는 부모 클래스의 컬럼을 자식 테이블마다 복사해서 저장하기 때문입니다.

먼저 상속 구조를 보겠습니다.

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

    @Id
    private Long id;

    private String name;
    private int price;
}
 

자식 클래스:

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

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

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

1. TABLE_PER_CLASS 방식의 테이블 구조

이 전략에서는 부모 테이블(Item)을 만들지 않습니다.

대신 자식 클래스마다 독립적인 테이블을 만듭니다.

결과:

BOOK 테이블

IDNAMEPRICEAUTHOR
1 JPA책 30000 김영한

ALBUM 테이블

IDNAMEPRICEARTIST
2 BTS앨범 15000 BTS

MOVIE 테이블

IDNAMEPRICEDIRECTOR
3 아바타 12000 제임스카메론

여기서 중요한 점:

name, price, id는 원래 부모 Item의 속성입니다.

그런데 부모 테이블이 없기 때문에:

Item
 ├── id
 ├── name
 └── price
 

이 정보가 각각의 자식 테이블에 복사됩니다.

즉:

BOOK
----------------
id
name       ← Item에서 복사
price      ← Item에서 복사
author


ALBUM
----------------
id
name       ← Item에서 복사
price      ← Item에서 복사
artist


MOVIE
----------------
id
name       ← Item에서 복사
price      ← Item에서 복사
director
 

이것이 데이터 중복입니다.


2. 왜 이것이 문제가 될까?

예를 들어 Item에 새로운 공통 필드를 추가한다고 해봅시다.

 
private String manufacturer;
 

그러면:

BOOK 테이블:

 
ALTER TABLE BOOK ADD manufacturer varchar(255);
 

ALBUM 테이블:

 
ALTER TABLE ALBUM ADD manufacturer varchar(255);
 

MOVIE 테이블:

 
ALTER TABLE MOVIE ADD manufacturer varchar(255);
 

모든 테이블을 수정해야 합니다.


3. 조회도 문제가 됩니다.

예를 들어:

 
List<Item> items = itemRepository.findAll();
 

"모든 상품 조회"를 하고 싶다면 어떻게 해야 할까요?

Item 테이블이 없습니다.

그래서 Hibernate는 내부적으로:

 
SELECT * FROM BOOK
UNION ALL
SELECT * FROM ALBUM
UNION ALL
SELECT * FROM MOVIE
 

같은 SQL을 만들어야 합니다.

테이블이 많아질수록 성능 문제가 생깁니다.


4. 세 가지 전략 비교

전략테이블 구조장점단점
SINGLE_TABLE 부모+자식 모두 하나의 테이블 조회 빠름 컬럼 낭비 가능
JOINED 부모 테이블 + 자식 테이블 정규화, 데이터 중복 없음 JOIN 필요
TABLE_PER_CLASS 자식 테이블만 존재 구조 단순 중복 발생, 조회 어려움

5. 그래서 실무에서는?

대부분:

 
@Inheritance(strategy = InheritanceType.JOINED)
 

또는

 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 

을 사용합니다.

특히 JPA를 설명하는 예제(예: 상품(Item), Book, Album, Movie)에서는 보통:

  • JOINED → 객체지향 모델과 DB 정규화 관점에서 좋은 선택
  • SINGLE_TABLE → 성능과 단순성 때문에 많이 사용

이고,

TABLE_PER_CLASS는 거의 사용하지 않는 전략입니다.