che01 님의 블로그
JPA 상속관계 매핑 전략 완전 정리 본문
상속이란 무엇인가?
프로그래밍에서 상속은 부모와 자식 관계를 만드는 것입니다. 예를 들어 "상품"이라는 부모가 있고, "책"과 "옷"이라는 자식들이 있다고 생각해보세요.
- 상품: 이름, 가격 (공통 속성)
- 책: 이름, 가격 + 저자 (상품 + 고유 속성)
- 옷: 이름, 가격 + 사이즈 (상품 + 고유 속성)
하지만 여기서 문제가 생깁니다. Java는 상속이 가능하지만, 데이터베이스 테이블은 상속 개념이 없어요!
그래서 JPA는 Java의 상속 구조를 데이터베이스 테이블로 변환하는 3가지 방법을 제공합니다.
전략 1: 단일 테이블 전략 (Single Table)
"모든 걸 하나의 테이블에 때려넣자!"
어떻게 동작하나요?
부모와 모든 자식 클래스의 정보를 하나의 테이블에 저장합니다. 어떤 타입인지 구분하기 위해 DTYPE이라는 컬럼을 추가해요.
코드 예제
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
public abstract class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
}
@Entity
@DiscriminatorValue("BOOK")
public class Book extends Product {
private String author;
}
@Entity
@DiscriminatorValue("COAT")
public class Coat extends Product {
private Integer size;
}
결과 테이블
id name price author size dtype
| 1 | 스프링 완전정복 | 25000 | 김영한 | null | BOOK |
| 2 | 겨울 코트 | 150000 | null | 95 | COAT |
장단점
장점:
- 쿼리가 빠름 (조인 필요 없음)
- 테이블 관리가 간단
단점:
- null 값이 많이 생김
- 테이블이 복잡해질 수 있음
전략 2: 조인 전략 (Joined)
"각자 테이블을 만들고 필요할 때 합치자!"
어떻게 동작하나요?
부모 클래스와 자식 클래스를 각각 별도 테이블로 만듭니다. 데이터를 조회할 때는 JOIN으로 합쳐서 가져와요.
코드 예제
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "dtype")
public abstract class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
}
@Entity
@DiscriminatorValue("BOOK")
public class Book extends Product {
private String author;
}
@Entity
@DiscriminatorValue("COAT")
public class Coat extends Product {
private Integer size;
}
결과 테이블들
Product 테이블:
id name price dtype
| 1 | 스프링 완전정복 | 25000 | BOOK |
| 2 | 겨울 코트 | 150000 | COAT |
Book 테이블:
id author
| 1 | 김영한 |
Coat 테이블:
id size
| 2 | 95 |
장단점
장점:
- 정규화된 구조 (중복 데이터 없음)
- 데이터 무결성이 좋음
단점:
- 조인으로 인한 성능 저하 가능
- 쿼리가 복잡해짐
전략 3: 구현 클래스마다 테이블 전략 (Table Per Class)
"각자 완전히 독립된 테이블을 만들자!"
어떻게 동작하나요?
부모 테이블은 만들지 않고, 자식 클래스마다 완전한 독립 테이블을 만듭니다. 각 테이블에는 부모의 속성도 모두 포함돼요.
코드 예제
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private BigDecimal price;
}
@Entity
public class Book extends Product {
private String author;
}
@Entity
public class Coat extends Product {
private Integer size;
}
결과 테이블들
Book 테이블:
id name price author
| 1 | 스프링 완전정복 | 25000 | 김영한 |
Coat 테이블:
id name price size
| 2 | 겨울 코트 | 150000 | 95 |
장단점
장점:
- 각 테이블이 완전히 독립적
- 구조가 단순함
단점:
- 중복 컬럼이 많이 생김
- 부모 타입으로 조회하기 어려움
언제 어떤 전략을 써야 할까?
상황별 추천
상황 추천 전략 이유
| 성능이 가장 중요할 때 | 단일 테이블 전략 | 조인 없이 한 번에 조회 |
| 데이터 정확성이 중요할 때 | 조인 전략 | 정규화로 데이터 무결성 보장 |
| 각 타입이 완전히 독립적일 때 | 구현 클래스 전략 | 테이블 간 의존성 없음 |
전략별 비교표
전략 성능 저장공간 구조 복잡도 추천도
| 단일 테이블 | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| 조인 전략 | ⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
| 구현 클래스 | ⭐⭐ | ⭐ | ⭐⭐⭐ | ⭐ |
실무에서는?
대부분의 경우 단일 테이블 전략이나 조인 전략을 사용합니다.
- 단순하고 빠른 서비스: 단일 테이블 전략
- 복잡하고 정확성이 중요한 서비스: 조인 전략
구현 클래스 전략은 특별한 경우가 아니면 잘 사용하지 않습니다.
마무리
JPA의 상속관계 매핑은 객체지향의 상속을 관계형 데이터베이스에서 어떻게 표현할지에 대한 해답입니다. 각 전략의 특징을 이해하고 상황에 맞게 선택하는 것이 중요합니다!
'Spring' 카테고리의 다른 글
| Self-invocation Problem (0) | 2025.06.24 |
|---|---|
| JPA N+1 문제 완벽 해결하기 - EntityGraph를 활용한 성능 최적화 (0) | 2025.06.10 |
| JPA N:M 관계 완전 정리 (0) | 2025.06.09 |
| JPA 1:1 관계 완전 정리 (0) | 2025.06.09 |
| JPA 1:N 관계 완전 정리 (0) | 2025.06.09 |