🗄️ TypeORM2. Relations (1:1 · 1:N · N:M)📖 개요

02 — Relations (관계)

질문: TypeORM에서 두 엔티티 사이의 관계를 정확히 표현하려면 어디까지 결정해야 하는가? 한 줄 답: 관계는 데코레이터 쌍(예: @OneToMany/@ManyToOne) + @JoinColumn/@JoinTable 위치(외래키가 어느 테이블에 사느냐) + 로딩 전략(eager/lazy/명시 relations) — 이 세 축이 동시에 맞아야 한 번에 동작한다.

이전 챕터(01 — Entity & 데코레이터)가 한 테이블을 클래스로 박는 법이었다면, 이번 챕터는 두 테이블 사이의 외래키를 데코레이터로 박는 법이다. 한 줄로 끝나는 이야기처럼 보이지만, TypeORM 사용자의 가장 흔한 버그가 모두 여기서 나온다 — 방향, 외래키 위치, 로딩 시점. 세 축을 분리해서 본다.


챕터 지도


읽는 순서

순서문서누구에게
101-one-to-one가장 단순한 관계부터 — 비대칭성의 출발
202-one-to-many-and-many-to-one가장 흔한 관계 — 90%의 실전 케이스
303-many-to-many조인 테이블이 추가 컬럼을 가져야 하는 순간
404-eager-vs-lazy관계는 언제 로드할 것인가 — 함정 챕터
505-cascade-and-orphanTypeORM의 cascade는 애플리케이션 레벨이다
606-self-referencing-and-polymorphic트리 구조와, TypeORM이 지원하지 않는 polymorphic

추천 동선: 처음 TypeORM을 만지면 1·2·3 → 4·5는 반드시 → 6은 트리/STI를 다루게 될 때 돌아오면 된다.


6개 문서 한 줄 요약

#문서한 줄 답
01OneToOne@JoinColumn한 쪽 엔티티에만 붙는다 — 그 쪽 테이블이 외래키의 주인이다.
02OneToMany & ManyToOne두 데코레이터는 항상 쌍으로 정의되고, FK는 Many 쪽 테이블에 산다.
03ManyToMany@JoinTable한 쪽만에 붙는다 — 추가 컬럼이 필요하면 중간 엔티티 패턴으로 분해한다.
04Eager vs Lazyeager: true모든 find가 join을 끌고 다니게 만든다 — N+1로 가는 가장 빠른 길.
05Cascade & OrphanTypeORM cascadesave() 시점에 동작하는 애플리케이션 레벨 전파다 — DB의 ON DELETE CASCADE와 다르다.
06Self-ref & PolymorphicTypeORM은 트리는 4가지 패턴으로 지원하지만, polymorphic은 1급 시민이 아니다.

왜 이 챕터가 중요한가

관계 한 줄을 잘못 적으면 세 가지 방식으로 실패한다.

흔한 실수결과어느 문서가 답하는가
@JoinColumn을 양쪽에 다 붙임마이그레이션 시 FK가 두 개 생성됨01
@ManyToOne만 정의하고 반대편 @OneToMany를 빠뜨림반대 방향 탐색 불가user.posts 못 씀02
@JoinTable을 양쪽에 붙임TypeORM이 조인 테이블을 두 개 생성 시도 → 에러03
eager: true를 깊은 관계에 줌모든 find가 User → Posts → Comments → Author join04
cascade: true만 믿고 DB ON DELETE를 안 검애플리케이션 레벨에서만 동작 — raw SQL/migration 우회 시 깨짐05
트리 구조에 그냥 @ManyToOne(() => Self)만 씀깊은 트리 재귀 쿼리 못 함06

이 챕터는 *세 축(데코레이터 쌍 · JoinColumn 위치 · 로딩 전략)*을 분리하면 이 실수들이 모두 체계적으로 사라진다는 것을 보여준다.


세 축을 한 표로

관계 유형데코레이터 (소유 쪽)데코레이터 (반대)@JoinColumn/@JoinTable 위치FK가 사는 테이블
OneToOne@OneToOne + @JoinColumn@OneToOne (옵션)소유 쪽에만소유 쪽
OneToMany / ManyToOne@ManyToOne@OneToMany(자동, @JoinColumn은 옵션)Many 쪽 (항상)
ManyToMany@ManyToMany + @JoinTable@ManyToMany소유 쪽에만제3의 조인 테이블

기억할 한 줄: @JoinColumn이 붙은 쪽이 FK의 주인이고, FK는 항상 one을 가리키는 many 쪽에 산다.


다음 챕터로

이 챕터에서 관계를 어떻게 선언하는가를 끝내면, 다음은 그 관계를 어떻게 SQL로 가져오는가다. find({ relations: { posts: true } }), queryBuilder.leftJoinAndSelect, Repository.save(cascadeOptions) — 모두 03 — Repository & DataMapper에서.

그리고 관계의 로딩 전략 한 줄이 어떻게 프로덕션 N+1 사고가 되는지는 07 — 성능 & N+1에서 끝장을 본다.

다음: 01 — OneToOne — 가장 단순한 관계에서 비대칭성을 본다.