[NestJS] - 8. 데이터베이스 관계와 TypeORM 데이터베이스 관계 매핑

2023. 2. 22. 11:52
반응형

이전 포스팅에서 ORM을 통해서 데이터베이스의 테이블을 생성해 보았는데, 이번에는 TypeORM을 통해 테이블 간의 관계 설정하는 방법을 포스팅하려고 한다.

 

 


데이터베이스 관계

데이터베이스의 관계는 Foreign Key(외래키)를 통해 형성되며, 종류는 크게 세가지로 나뉜다.

 

1 : 1 관계

중고마켓에서 사용자가 상품을 등록할 때 거래 장소를 입력하게 된다. 상품은 단 하나의 상품거래 위치를 가지게 되며, 상품거래 위치 또한 하나의 상품과만 관계를 가지게 되므로 1:1 관계이다.

 

 


 

N : 1 관계

중고마켓에서 사용자가 상품을 등록할 때 노출될 카테고리 위치 하나를 선택하게 된다. 

상품은 한개의 카테고리만을 가질 수 있으나(1), 카테고리는 여러개의 상품과 관계(N)를 가질 수 있다. (위의 그림에서 한개에서 뻗어나가는 선이 여러개이다)

따라서 상품 : 카테고리 = N : 1 의 관계를 가지게 된다.

 

 


 

N : M 관계

 

인스타그램과 같이 판매자는 상품을 등록할 때 여러개의 태그를 설정할 수 있도록 하고, 구매자는 태그를 포함한 연관 상품을 찾을 수 있게 하려고 한다.

상품은 여러개의 태그 설정을 할 수 있으며(N), 태그또한 여러개의 상품과 관계(M)를 가질 수 있다. (상품과 태그 두쪽 모두 뻗어나가는 화살표가 여러개이다.)

따라서 상품 : 상품태그 = N : M 의 관계를 가지게 된다.

 

 

 

 

일반적으로 데이터베이스에서의 N:M 관계는 사이에 좋은 성능을 위해서 중간 테이블을 둔 형태로 설계된다.

다음과 같이 중간 테이블을 둔다면, 상품 : 중간테이블 = 1:N  / 상품태그 : 중간테이블 = 1:N의 형태로 관계가 형성된다.

대부분의 관계형 데이터베이스들은 해당 중간 테이블을 만드는 것을 관계 설정 시 자동으로 지원해준다.

 

 


TypeORM을 통한 관계 생성

이제 ORM을 통해서 엔티티 간의 관계 매핑을 해보자

 

 


1 : 1 관계

▶product.entity.ts

@Entity()
export class Product {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  // ...
  
  //1:1 엔터티 연결
  @JoinColumn()
  @OneToOne(() => ProductSalesLocation)
  productSalesLocation: ProductSalesLocation;

}

상품과 상품거래 위치의 관계를 작성하였다. 

@JoinColumn()은 FK임을 명시하여 관계를 형성하며, 해당 엔티티에서 FK를 통해 Join을 할 수 있도록 해준다.

@OneToOne()은 1:1 관계를 형성하며, 인수로는 익명 함수로 상품거래 위치 모델을 다음과 같은 형태로 넘겨준다.

 

 


1 : N 관계

▶product.entity.ts

@Entity()
export class Product {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  // ...
  
  //N(상품): 1(카테고리) 엔터티 연결
  //Many의 테이블에서는 JoinColumn 생략해도 One의 테이블 Join 가능
  @ManyToOne(() => ProductCategory)
  productCategory: ProductCategory;

  //N(상품): 1(유저) 엔터티 연결
  @ManyToOne(() => User)
  user: User;

}

상품과 사용자, 카테고리의 관계를 설정해주었다.

@ManyToOne() 데코레이터를 통해 관계를 형성하며, 인수로는 익명 함수로 관계를 형성할 대상 엔티티 모델을 넘겨준다.

TypeORM에서는 Many to One 관계에서는 @JoinColumn()을 생략해도 연관된 One을 Join 가능하다.

 

 


N:M 관계

▶product.entity.ts

@Entity()
export class Product {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  // ...
  
  //N(상품): M(상품태그) 엔터티 연결, ProductTags가 나를 찾을 때 인식하는 방법을 무명 함수로 인자 넣음
  //JoinTable은 N:M 관계의 중간 테이블을 자동 생성해주며, 기준 테이블 한쪽에만 작성하면 됨
  @JoinTable()
  @ManyToMany(() => ProductTag, (productTags) => productTags.products)
  productTags: ProductTag[];

}

▶productTag.entity.ts

@Entity()
export class ProductTag {
  //...

  //N(상품): M(상품태그) 엔터티 연결, Product가 나를 찾을 때 찾을 방법을 무명 함수로 인자 넣음
  @ManyToMany(() => Product, (products) => products.productTags)
  products: Product[];
}

상품과 상품태그의 관계를 설정해주었다.

@JoinTable() 데코레이터는 N:M 중간 테이블을 자동 작성해주며, 기준으로 잡은 테이블 한 쪽에만 작성해주면 된다.

@ManyToMany 데코레이터를 통해 관계를 형성하며, 인수로는 익명 함수로 관계를 형성할 대상 엔티티 모델과 함께 대상이 나를 찾을 수 있는 방법을 같이 넘겨주어야 한다. 

 

(productTags) => productTags.products)

- product 에서는 productTag가 products를 통해 나를 Join할 수 있음을 알려주었다.

 

(products) => products.productTags)

- productTags 에서는 product가 productTags를 통해 나를 Join할 수 있음을 알려주었다.

 


 

이렇게 ORM을 통해 관계 매핑까지 완료하였다. 다음 장에서는 ORM을 통해서 SQL CRUD 하는 방법에 대해서 포스팅해보겠다.

반응형

BELATED ARTICLES

more