목차
1. 구체 테이블 상속 (Concrete Table Inheritance)
2. 단일 테이블 상속 (Single Table Inheritance)
태그
#TYPESCRIPT
#MYSQL
#DB
#NODE
#TYPEORM
2025년 1월 2일 13:57

TypeORM은 엔터티 상속을 통해 코드 중복을 줄이고 데이터 모델을 효율적으로 관리할 수 있는 기능을 제공합니다. 주요 상속 전략으로는 구체 테이블 상속(Concrete Table Inheritance) 과 단일 테이블 상속(Single Table Inheritance) 이 있습니다.
1. 구체 테이블 상속 (Concrete Table Inheritance)
구체 테이블 상속은 공통 필드를 가진 여러 엔터티가 각각 독립된 테이블로 생성되는 방식입니다. 이를 통해 코드 중복을 줄이고 유지 보수를 용이하게 할 수 있습니다.
1-1. 코드
먼저, 공통 필드를 가진 추상 클래스를 정의합니다:
import { PrimaryGeneratedColumn, Column } from 'typeorm';
export abstract class Content {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
}
그런 다음, 이 추상 클래스를 상속받는 엔터티들을 정의합니다:
import { Entity, Column } from 'typeorm';
import { Content } from './Content';
@Entity()
export class Photo extends Content {
@Column()
size: string;
}
@Entity()
export class Question extends Content {
@Column()
answersCount: number;
}
@Entity()
export class Post extends Content {
@Column()
viewCount: number;
}
이렇게 하면 photo, question, post 세 개의 테이블이 생성되며, 각 테이블은 Content 클래스의 공통 필드와 자신만의 고유한 필드를 포함하게 됩니다
1-2. 생성된 테이블
| Photo Table |
|---|
| id (PK) |
| title |
| description |
| size |
| Question Table |
|---|
| id (PK) |
| title |
| description |
| answersCount |
| Post Table |
|---|
| id (PK) |
| title |
| description |
| viewCount |
1-3. 설명
- 각 자식 엔터티(
Photo,Question,Post)는 별도의 테이블로 생성됩니다. - 공통 필드(
id,title,description)는 모든 테이블에 반복됩니다.
2. 단일 테이블 상속 (Single Table Inheritance)
단일 테이블 상속은 여러 엔터티가 하나의 테이블에 저장되는 방식입니다. 이때 각 행은 특정 엔터티 유형을 식별할 수 있는 추가 컬럼을 가집니다.
2-1. 코드
먼저, 임베디드 엔터티를 정의합니다:
@Entity()
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
export class Content {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
}
그런 다음, 다른 엔터티에서 이를 포함시킵니다:
@ChildEntity()
export class Photo extends Content {
@Column()
size: string;
}
@ChildEntity()
export class Question extends Content {
@Column()
answersCount: number;
}
@ChildEntity()
export class Post extends Content {
@Column()
viewCount: number;
}
이렇게 하면 content라는 하나의 테이블이 생성되고, 모든 Photo, Question, Post 엔터티의 데이터가 이 테이블에 저장됩니다. 각 행은 type 컬럼을 통해 해당 데이터의 엔터티 유형을 식별할 수 있습니다.
2-2. 생성된 테이블
| Content Table |
|---|
| id (PK) |
| title |
| description |
| type |
| size |
| answersCount |
| viewCount |
2-3. 샘플 데이터 예시
| id | title | description | type | size | answersCount | viewCount |
|---|---|---|---|---|---|---|
| 1 | Photo1 | Desc1 | Photo | 15MB | NULL | NULL |
| 2 | Ques1 | Desc2 | Question | NULL | 5 | NULL |
| 3 | Post1 | Desc3 | Post | NULL | NULL | 100 |
2-4. 설명
- 모든 자식 엔터티(
Photo,Question,Post)는 하나의 테이블(Content)에 저장됩니다. type컬럼을 통해 각 행이 어떤 자식 엔터티인지 구분됩니다.- 불필요한
NULL값이 생길 수 있습니다.
3. 임베디드 엔티티 (Embedded Entities)
임베디드 엔티티는 상속과 유사하게 공통 필드를 재사용할 수 있는 방법입니다. 그러나 상속과 달리 데이터베이스에 별도의 테이블을 생성하지 않고, 필드를 포함하는 방식으로 구성됩니다.
3-1. 코드
먼저, 임베디드 엔터티를 정의합니다:
import { Column } from 'typeorm';
export class Audit {
@Column()
createdAt: Date;
@Column()
updatedAt: Date;
}
그런 다음, 다른 엔터티에서 이를 포함시킵니다:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { Audit } from './Audit';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column(() => Audit)
audit: Audit;
}
이렇게 하면 User 테이블에 createdAt과 updatedAt 컬럼이 포함되며, Audit 클래스의 필드들을 재사용할 수 있습니다.
3-2. 생성된 테이블
| User Table |
|---|
| id (PK) |
| name |
| audit_createdAt |
| audit_updatedAt |
3-3. 샘플 데이터 예시
| id | name | audit_createdAt | audit_updatedAt |
|---|---|---|---|
| 1 | Alice | 2024-06-01 | 2024-06-02 |
| 2 | Bob | 2024-06-03 | 2024-06-04 |
3-4. 설명
Audit는User테이블의 필드로 직접 추가됩니다.- 별도의 테이블이 생성되지 않고, 필드가 직접 부모 테이블에 포함됩니다.
- 접근은
audit.createdAt,audit.updatedAt과 같은 방식으로 가능합니다.
4. 비교 요약 테이블
| 전략 | 테이블 구조 | 필드 중복 | 유연성 | 쿼리 복잡도 | 데이터 정규화 |
|---|---|---|---|---|---|
| Concrete Table | 개별 테이블 | 있음 | 높음 | 낮음 | 높음 |
| Single Table | 하나의 테이블 | 없음 | 중간 | 높음 | 낮음 |
| Embedded Entity | 부모 테이블 내 | 없음 | 낮음 | 낮음 | 높음 |
5. 전략 선택 가이드
- Concrete Table Inheritance
- 각 하위 엔터티가 독립적인 테이블을 가져야 하는 경우.
- 유지보수가 용이하지만 필드 중복이 발생합니다.
- Single Table Inheritance
- 여러 엔터티가 동일한 테이블에 저장되어야 하는 경우.
- 쿼리가 복잡할 수 있고, NULL 값이 많아질 수 있습니다.
- Embedded Entity
- 필드 재사용이 필요하지만 독립적인 테이블이 필요하지 않은 경우.
- 간단한 구조에 적합합니다.
6. 마무리
TypeORM의 상속 전략은 프로젝트의 요구사항에 따라 선택해야 합니다.
- 복잡한 구조와 명확한 분리를 원한다면:
Concrete Table Inheritance - 단순한 구조와 유연한 쿼리를 원한다면:
Single Table Inheritance - 필드 재사용이 주목적이라면:
Embedded Entity
참고 사이트