🗄️ TypeORM1. Entity & 데코레이터📖 개요

01 · Entity & 데코레이터

이 챕터가 답하는 질문: TypeORM의 @Entity/@Column 한 줄은 런타임에 무엇을 박고, 컴파일 타임에 무엇을 강제하며, DB에는 어떤 DDL을 남기는가? 한 줄 답 (Pyramid Top): @Entity 한 줄이 런타임 메타데이터 + 컴파일 타임 타입 + DDL 생성 세 가지를 동시에 한다 — 이 챕터는 그 한 줄을 6개의 결정으로 분해한다.”


챕터 지도 (Mermaid)


Why — 왜 이 챕터를 먼저 보나

TypeORM 사용자의 첫 1주일 사고 대부분이 이 챕터에서 나온다.

  • “왜 @Column() name: string에서 마이그레이션이 varchar 아닌 text로 떨어지지?” → 컬럼 타입 추론이 일부만 동작 → 02-column-types
  • “왜 @PrimaryGeneratedColumn('uuid')로 했더니 PG에선 되는데 MySQL에선 다른 동작이지?” → 드라이버별 시퀀스가 다름 → 03-primary-keys
  • @DeleteDateColumn 박았는데 왜 find삭제된 row도 가져오지?” → soft delete는 find의 의미를 바꾼다04-special-columns
  • “왜 @Embedded 한 객체가 컬럼 prefix로 펼쳐지지?” → embedded는 값 객체의 표현이지 조인이 아님 → 05-embedded-and-inheritance
  • “데코레이터를 쓰면 DI/IoC와 충돌한다는데 어떻게 피하지?” → EntitySchema클래스 외부에 메타를 적는다 → 06-entity-schema-alternative

이 챕터는 Entity라는 한 단어 안에 숨어 있는 6개의 결정에 이름을 붙인다.


How — 어떻게 읽나

#문서읽는 데핵심 키워드
0101-entity-decorator8분@Entity, name, schema, database, synchronize
0202-column-types12분@Column, type, nullable, default, length, unique, comment
0303-primary-keys10분@PrimaryColumn, @PrimaryGeneratedColumn, increment/uuid/rowid/identity, 복합키
0404-special-columns12분@CreateDateColumn, @UpdateDateColumn, @DeleteDateColumn, @VersionColumn
0505-embedded-and-inheritance12분@Embedded, @TableInheritance, @ChildEntity, STI vs CTI
0606-entity-schema-alternative10분EntitySchema, schema-first, 데코레이터 회피

의존성: 01은 모든 것의 입구. 02·03·04는 컬럼 차원의 결정이라 같은 층에 있다. 05는 02 위에 쌓이는 조합 결정. 06은 대안이라 마지막.


What — 한 페이지 요약

문서한 줄 결론
01@Entity클래스를 테이블로 등록하는 한 줄이다 — name/schema/database 인자가 어디에 만들지를 결정한다.
02@Column 타입은 TypeScript 원시 타입만 자동 추론된다 — Date·enum·decimal·json명시해야 한다.
03@PrimaryColumn수동 PK, @PrimaryGeneratedColumn자동 PK'increment'/'uuid'/'rowid'/'identity' 4 전략 중 DB와 분산성이 답을 정한다.
04@CreateDateColumn·@UpdateDateColumn·@DeleteDateColumn·@VersionColumn 네 줄이 시간·삭제·동시성을 동시에 해결한다 — 그러나 @DeleteDateColumnfind의 의미를 바꾼다.
05@Embedded값 객체컬럼 prefix로 펼치고, STI는 한 테이블에 type 컬럼, CTI는 부모/자식 테이블 분리. 셋은 다른 트레이드오프를 산다.
06EntitySchema데코레이터 없이 같은 메타데이터를 적는 길이다 — DI 컨테이너 충돌이나 POJO 도메인 모델 유지에 쓴다.

What-if — 이 챕터를 건너뛰면

  • @Column() 한 줄을 모르면: TS 타입과 DB 타입의 불일치를 매번 마이그레이션에서 마주친다.
  • PK 전략을 모르면: 'increment'로 시작했다 수평 분할 시점에 UUID로 갈아엎는다.
  • @DeleteDateColumn의 부작용을 모르면: 운영 데이터가 사라진 줄 알고 다시 insert → unique constraint violation.
  • Embedded·상속을 모르면: 같은 컬럼 묶음을 5개 테이블에 반복 정의한다.
  • EntitySchema를 모르면: 도메인 모델이 TypeORM에 강결합되어 클린 아키텍처 시도가 막힌다.

Insight — 한 단락 이야기

@Entity 한 줄은 사실 3개의 책임을 동시에 진다”

2016년 Umed Khudoiberdiev가 TypeORM 첫 커밋을 적을 때 — Java의 JPA, C#의 EF가 영감이었다. 그러나 JPA는 @Entity런타임 메타만 박고 DDL은 ddl-auto 옵션이 별도로 만든다. TypeORM은 그것을 한 데코레이터로 묶었다. 결과: 학습 비용은 낮아졌지만, 세 책임이 한 줄에 모인 결정이 나중에 풀기 어려운 결합이 됐다. synchronize: true프로덕션 폭탄이 된 이유, EntitySchema대안으로 살아남은 이유 모두 이 한 줄에서 시작한다. 편의는 항상 결합의 비용을 청구한다 — 단지 영수증이 늦게 올 뿐이다.


한 단락 요약

Entity는 런타임 메타 + 컴파일 타입 + DDL 생성의 세 책임을 한 데코레이터로 묶은 TypeORM의 핵심 결정이다. 이 챕터를 끝내면 @Entity({ name: 'users' }) class User { @PrimaryGeneratedColumn('uuid') id: string } 한 블록에 들어 있는 6개의 결정(테이블명·PK 전략·UUID 생성 위치·컬럼 타입 추론·인덱스·마이그레이션 영향)을 분리해서 읽을 수 있다. 다음 챕터(02-relations)는 이 엔티티들을 어떻게 잇는가를 다룬다.