안녕하세요 반갑습니다.
오늘은
- erd 변경사항과 수정 이유
- typeORM 소개 및 적용
- DB 관계 정의하기에 대해 알아보겠습니다.
1. 같은 테이블을 바라보기 때문에 중복된 컬럼을 삭제했습니다.
기존의 연결테이블입니다. 여기서 script_id는 food_id와 동일한 주문 테이블을 바라보기 때문에 food_id와 ingredient_id 를 order_id로 수정하여 컬럼이 중복되는 것을 방지하고자 수정했습니다.
수정한 테이블로 typeorm을 적용해보겠습니다.
2. TypeORM(Object Relational Mapping)이란?
객체와 관계형 데이터베이스의 데이터를 자동으로 변형 및 연결하는 작업입니다.
ORM을 이용한 개발은 객체와 데이터베이스의 변형에 유연하게 사용할 수 있기 때문에 이번 프로젝트에 도입하기로 결정했습니다.
2-1. TypeORM 적용하기 위해 설치해야하는 모듈들
typeorm 라이브러리 설치
npm install @nestjs/typeorm —save
NestJS에서 TypeORM을 사용하기 위해 연동시켜주는 모듈입니다.
2-2. typeORM 애플리케이션에 연결하기
- typeORM 설정파일 생성
2. typeORM 설정파일 작성
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export const typeORMConfig: TypeOrmModuleOptions = {
type: '사용하는 db',
host: 'localhost',
port: 3306,
username: '설정값 넣어주기',
password: '설정값 넣어주기',
database: 'app',
entities: [__dirname + '/../**/*.entity.{js,ts}'],
synchronize: true,
};
- entities: 엔티티를 이용해서 데이터베이스 테이블을 생성해 준다. 그래서 엔티티 파일이 어디 있는지 설정해 줍니다.
- synchronize: true 값을 주면 애플리케이션을 다시 실행할 때 엔티티 안에서 수정된 컬럼의 길이 타입 변경값 등을 해당 테이블을 drop 한 후 다시 생성해 줍니다.
3. 루트 모듈에서 import 해준다.
forRoot 안에 넣어준 설정(configuration)은 모든 Sub-Module 부수적인 모듈들에 다 적용이 됩니다.
3. typeORM 작성하기
저는 주문 테이블, 재료 테이블, 이 두 개의 테이블을 연결하는 연결테이블 총 3가지 테이블을 typeorm으로 작성해 보겠습니다. 해야 할 일은 주문테이블-연결테이블을 1:N으로 나타내어야 하고, 재료테이블-연결테이블을 1:N으로 나타내어야 합니다.
주문 테이블
import {
BaseEntity,
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { Connect } from './connect.entity';
@Entity('Order')
export class Order extends BaseEntity {
@PrimaryGeneratedColumn({ type: 'bigint' })
id: bigint;
@Column({ type: 'varchar', length: 200, nullable: true })
name: string;
@Column({ type: 'varchar', length: 10, default: 'pizza' })
type: string;
@Column({ type: 'text' })
script: string;
@OneToMany(() => Connect, (connect) => connect.order, { cascade: true })
connects: Connect[];
@CreateDateColumn({ type: 'datetime' })
created_at: Date;
@UpdateDateColumn({ type: 'datetime' })
updated_at: Date;
@DeleteDateColumn({ type: 'boolean', default: false })
deleted: boolean;
}
- Order이름을 가진 테이블의 id를 @PrimaryGeneratedColumn 값으로 주어 auto_increment가 되게 하였습니다.
- 하나의 주문에 대해 여러 개의 재료를 가진 연결테이블을 불러오기 위해 1:N으로 구성하여 @OneToMany 데코레이션을 사용했습니다.
재료 테이블
import {
BaseEntity,
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { Connect } from './connect.entity';
@Entity('Ingredient')
export class Ingredient extends BaseEntity {
@PrimaryGeneratedColumn({ type: 'bigint' })
id: number;
@Column({ type: 'varchar' })
name: string;
@Column({ type: 'tinyint' })
oven: number;
@Column({ type: 'varchar' })
type: string;
@OneToMany(() => Connect, (connect) => connect.ingredient, {
cascade: true,
})
connects: Connect[];
@CreateDateColumn({ type: 'datetime' })
created_at: Date;
@UpdateDateColumn({ type: 'datetime' })
updated_at: Date;
@DeleteDateColumn({ type: 'boolean', default: false })
deleted: boolean;
}
- Ingredient이름을 가진 테이블의 id를 @PrimaryGeneratedColumn 값으로 주어 auto_increment가 되게 하였습니다.
- 또한 연결테이블과 1:N 관계를 표현하기 위해 @OneToMany 데코레이터를 써서 connect의 ingredient가 외래값으로 사용된 것을 나타내었습니다.
연결테이블
import {
BaseEntity,
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { Order } from './order.entity';
import { Ingredient } from './ingredient.entity';
@Entity('Connect')
export class Connect extends BaseEntity {
@PrimaryGeneratedColumn({ type: 'bigint' })
id: number;
@ManyToOne(() => Order, (order) => order.connects, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'order_id' })
order: Order;
@ManyToOne(() => Ingredient, (Ingredient) => Ingredient.connects, {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'ingredient_id' })
ingredient: Ingredient;
@Column({ type: 'tinyint', default: 0 })
side: number;
@CreateDateColumn({ type: 'datetime' })
created_at: Date;
@UpdateDateColumn({ type: 'datetime' })
updated_at: Date;
@DeleteDateColumn({ type: 'boolean', default: false })
deleted: boolean;
}
- @ManyToOne(() => Order, (order) => order.connects, { onDelete: 'CASCADE' }) : Connect 엔티티에서 Order 객체를 참조하고 DB에서는 Connect 테이블의 order_id라는 이림의 외래키가 생성되고 order_id는 Order테이블의 id를 참조합니다.
- @ManyToOne(() => Ingredient, (Ingredient) => Ingredient.connects, {onDelete: 'CASCADE'}): Connect 엔티티에서 Ingredient 객체를 참조하고 DB에서는 Ingredient 테이블의 ingredient_id라는 이림의 외래키가 생성되고 ingredient_id는 Ingredient테이블의 id를 참조합니다.
이번에 DB연결관계를 정의하면서 가장 어려웠던 점은 재료-연결 테이블이 N:M이 왜 아닐까? 였습니다.
저는 재료 N개에 대하여 여러 가지로 연결될 수 있고 , 여러 개의 연결에는 여러개의 재료가 필요하기 때문에 N:M 관계로 생각했었습니다.
그럼에도 재료-연결 테이블이 1:N인 이유는 id가 1번인 채로 여러개의 ingredient_id가 못 오기 때문이었습니다.
(예를 들어, id(1)-ingredient_id(3,4,5,6) 이렇게 오는 것이 아니라 id(1)-ingredient_id(3), id(1)-ingredient_id(4), id(1)-ingredient_id(5), id(1)-ingredient_id(6) 이렇게 하나씩 대응됨)
또한 ingredient_id 관점에서 볼 때 여러 개의 재료를 동시에 보낼 수 있기 때문에(예를 들어, ingredient_id(1)- id(1) , ingredient_id(1)-id(2)) 1:N 관계였습니다.
결과
다음 글로는..
다음 글로는 DB에 데이터를 주입하는 내용으로 찾아뵙겠습니다 !
긴 글 읽어주셔서 감사합니다. 다음 글에서 뵙겠습니다. 😊
'포트폴리오' 카테고리의 다른 글
TypeORM에서의 @DeletedDateColumn를 활용한 Soft Delete 관련 트러블슈팅 (0) | 2025.02.20 |
---|---|
좋은 피자 위대한 피자 (1) (프로젝트 소개, ERD 정리) (0) | 2025.01.06 |