일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 타입스크립트
- 자바스크립트
- 타입 챌린지
- Crawling
- 문자열
- socket
- 쉬운 문제
- Algorithm
- 수학
- ip
- TCP
- 크롤링
- typescript
- 프로그래머스 레벨 2
- 그래프
- type challenge
- Node.js
- 소켓
- 프로그래머스
- 가천대
- javascript
- dp
- BFS
- 알고리즘
- Nestjs
- HTTP
- dfs
- 레벨 1
- 백준
- HTTP 완벽 가이드
- Today
- Total
kakasoo
[TypeORM] 1. Column과 Entity, 일대 다 관계의 표현 본문
특별한 Column Decorator
import { ApiProperty } from '@nestjs/swagger';
import { DeleteDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
import { CreateAtEntity } from './create-at.entity';
export class CommonEntity extends CreateAtEntity {
@PrimaryGeneratedColumn()
id: number;
@UpdateDateColumn()
updatedAt: Date;
@DeleteDateColumn({ nullable: true })
deletedAt: Date;
}
TypeORM에서 Entity는 데이터베이스의 각 테이블을 JavaScript 코드로 해석한 것이에요.
그래서 @Column() 데코레이터를 통해서 각각의 칼럼들이 무엇을 의미하는지를 서술
합니다.
@Column() 외에도 createdAt, updatedAt, deletedAt을 나타내기 위한 특별한 데코레이터
들도 있습니다.
TypeORM에는 각각 @CreateDateColumn, @UpdateDateColumn, @DeleteDateColumn
을 의미합니다.
또한 PK ( = Primary Key )를 나타내기 위한 @PrimaryGeneratedColumn
도 있습니다.
다만 이런 칼럼들은 모두 @Column() 데코레이터를 확장한 것이기 때문에, Column을 이해하는 게 더 중요합니다.
Column Decorator’s parameter
@Column('int4')
int: number;
TypeORM에서의 Column은 Column 데코레이터로 표현되며, 데코레이터의 첫 인자는 타입입니다.
@Column()
int: number;
하지만 TypeScript의 타입이 있다면 별도의 타입 명시를 하지 않더라도 int, varchar, bool 등이 들어갑니다.
@Column({ name: 'int_column_name' })
int: number;
만약 name property에 이름을 명시해준다면, code 상에서는 ‘int’ 지만, DB에서는 다른 이름임을 알려줄 수 있습니다.
TypeORM 역시 ORM의 의미인 Object Relation Mapping에 충실하게 구현되어 있습니다.
DB가 실제로 어떠하든, Node.js 객체로 구현된 클래스를 보고 DB 로직을 작성할 수 있게 되어 있습니다.
@Column({ nullable: true, unique: true, select: false })
int: number;
그 외에도 null을 허용할지를 결정하는 nullable과 테이블 내에서 유일한 값을 의미하는 unique 설정이 있고,
추후 이야기할 select 문에서 별도의 명시 없이는 자동으로 잡히지 않게 해주는 select 설정이 있습니다.
그 외에도 특정 DB에서 사용 가능한 array나 prefix, comment 등 재미있는 설정들이 다수 있습니다.
다만 이 설정들은, 필요한 시점에 배워도 늦지 않습니다.
Entity는 이렇게, 자바스크립트 클래스에 @Column() 데코레이터를 덧붙여 DB와 매핑한 것에 불과합니다.
Column을 가진 Entity 예시
import { Column, Entity, OneToMany } from 'typeorm';
import { CommonEntity } from '../common/common.entity';
import { ProductEntity } from './product.entity';
@Entity()
export class SellerEntity extends CommonEntity {
@Column({ comment: '대표자 이름' })
ownerName: string;
@Column({ comment: '브랜드 이름' })
brandName: string;
@Column({ comment: '사업자번호' })
brn: string;
@Column({ length: 15, nullable: true, comment: '전화번호' })
phone: string;
}
이제 이 클래스를 해석할 수 있게 되었습니다.
각각의 칼럼들은 이름이 동일한 이름으로 DB 매핑되어 Column의 name property는 생략되었습니다.
이 중 phone Column은 전체 길이가 최대 15를 넘지 않으며 null이 허용된 것을 알 수 있습니다.
또한 Entity는 JavaScript의 클래스기 때문에 상속을 통해 표현
될 수 있습니다.
이 Seller Entity는 id, createdAt, updatedAt, deletedAt을 상속을 통해 표현하고 있습니다.
모든 Entity에 동일하게 들어가는 칼럼이기 때문에 이처럼 표현하는 것이 중복을 줄이는 데에 용이합니다.
관계 매핑, Relations
OneToMany, ManyToOne
import { Column, Entity, OneToMany } from 'typeorm';
import { CommonEntity } from '../common/common.entity';
import { ProductEntity } from './product.entity';
@Entity()
export class SellerEntity extends CommonEntity {
@Column({ comment: '대표자 이름' })
ownerName: string;
@Column({ comment: '브랜드 이름' })
brandName: string;
@Column({ comment: '사업자번호' })
brn: string;
@Column({ length: 15, nullable: true, comment: '전화번호' })
phone: string;
/*
* below are relations
*/
@OneToMany(() => ProductEntity, (product) => product.seller)
products: ProductEntity[];
}
DB 테이블은 서로가 서로에게 조인될 수 있도록 외래키를 주고 받는 형태로 작성되곤 합니다.
여기서 Seller ( = 판매자 ) 는 자신이 판매하고 있는 물건이 여럿 있을 겁니다.
이 경우 판매자가 상품 id를 가지는 것은 테이블 내에서는 표현이 불가능하기 때문에,
각각의 상품이 seller의 id 값을 FK ( Foreign Key ) 로 가지는 게 더 적절합니다.
어떤 대상 A가 B를 다수 가지고 있다는 것은, B 입장에서는 B 다수가 하나의 A를 가진다는 것과도 같습니다.
그래서 A에게 B는 일대다 관계이고, B에게 A는 다대일 관계라고 말할 수도 있어요.
TypeORM에서는 이런 관계 표현을 OneToMany, ManyToOne Decorator로 표현
합니다.
@OneToMany(() => TargetEntity, (target) => taget.inverse)
@OneToMany(() => ProductEntity, (product) => product.seller)
OneToMany의 첫 번째 파라미터는 A ( = Seller )가 어느 Entity ( = Product )로 이어질 수 있는지를 나타냅니다.
그리고 두 번째 파라미터는 그 상대 Entity에서 A가 어떻게 표현되고 있는지를 담아줍니다.
우리는 아직 Product Entity를 보지 못했지만, Seller Entity에 표현된 역 (inverse)을 통해,
Product Entity Class에 seller 라는 이름의 프로퍼티가 정의되어 있다는 것을 짐작할 수 있습니다.
@ManyToOne과 @JoinColumn
import { Column, Entity, ManyToOne } from 'typeorm';
import { CommonEntity } from '../common/common.entity';
import { SellerEntity } from './seller.entity';
@Entity()
export class ProductEntity extends CommonEntity {
@Column()
sellerId: number;
@Column({ comment: '상품 이름' })
name: string;
/*
* below are relations
*/
@ManyToOne(() => SellerEntity, (seller) => seller.products)
@JoinColumn({ name: 'sellerId', referencedColumnName: 'id' })
seller: SellerEntity;
}
반대쪽의 Product Entity를 간략하게 표현하면 이런 구조로 되어 있습니다.
각각의 product는 sellerId를 가지고 있고, 이를 통해서 자신의 판매자가 누군지 찾아갈 수 있게 되었습니다.
그리고 앞에서 짐작하셨다시피, product 여럿이 하나의 seller를 가지기 때문에 seller는 배열이 아닙니다.
/*
* below are relations
*/
@OneToMany(() => ProductEntity, (product) => product.seller)
products: ProductEntity[];
앞서 Seller Entity에서 products를 ProductEntity[] 로 표현한 것과 비교하면 차이가 명백합니다.
이는 Seller 입장에서는 일대다 관계였기 때문에 seller 하나에 연결된 상품이 다수였기 때문에 그렇습니다.
반대로 Product Entity 입장에서는 Seller는 다대일 관계기 때문에 SellerEntity가 타입이 될 수 밖에 없습니다.
또 다른 큰 차이는 Product Entity에서는 @JoinColumn() 이라는 decorator를 가졌다는 점입니다.
JoinColumn Decorator는 쉽게 말해, 이 seller 라는 TagerEntity가 어떻게 연결되었는지를 의미합니다.
name은 현재 보고 있는 Entity에서 어떤 Column을 통해 연결되었는지를 의미합니다.
Product Entity는 sellerId를 가지고 있고, 이걸 통해서 seller를 찾아갈 수 있다고 말씀드렸어요.
그러니깐, Product Entity의 JoinColumn options의 name 프로퍼티는 sellerId가 됩니다.
referencedColumnName은 역으로 sellerId가, Seller Entity에서는 어떤 Column을 참조했는지를 뜻합니다.
sellerId는 Seller Entity의 id이므로, ‘id’가 값이 됩니다.
'프로그래밍 > TypeORM' 카테고리의 다른 글
4. WHERE과 subQuery, COUNT를 조건으로 걸기 (0) | 2023.01.10 |
---|---|
3. Repository find 메서드 완벽 정리 (0) | 2023.01.07 |
[TypeORM] 2. ManyToMany, 다대다 관계의 표현 (0) | 2023.01.04 |
쿼리빌더 최대한 없애기 / deletedAt을 잘 이용하기 (0) | 2022.06.07 |