기존에 DB를 Mysql
를 사용했었다. 대학교 때, mysql으로 DB 수업을 진행했기 때문에 친숙하기도 하고 항상 DB를 선택할 때 mysql
를 사용했기 때문이다. 이번에는 postgreSQL
를 한 번 사용해보려고 한다. postgreSQL
를 선택한 이유는 다음과 같다.
postgreSQL
은 SQL 쿼리와 JSON 형식 둘 다 사용 가능하다. 이번에 NoSQL으로 DB를 접근해보려고 해서postgreSQL
을 채택했다.- 그럼 NOSQL DB 중 가장 유명한
MongoDB
를 사용하면 되지 않냐고 생각할 수 있는데,MongoDB
는 단점으로 SQL 쿼리 언어를 지원하지 않는다.- 새로운 DB를 공부하고 싶었는데, 다음 순위가 높은 DB가
Redis
이다. redis는 대규모 트래픽에서 이점이 있지만, NOSQL을 지원하지 않는다.
즉, 다시 말하면 공부하는 입장에서 DB로 postgreSQL
를 채택하는 건 이점이 있다고 생각한다.
진짜 postgreSQL + pgAdmin 설치하는 것만 2시간이 뭐야 반나절 걸렸네.. 결국 tableplus로 처리완료 걸린 거 같다.. 초기 설정값 셋팅하는 건 항상 지옥같다..
TypeORM이란 nodejs에서 실행되고 Typescript로 작성된 객체 관계형 매퍼 라이브러리이다.
TypeORM 특징과 이점
- 모델을 기반으로 데이터베이스 테이블 체계를 자동으로 생성
- 데이터베이스에서 객체를 쉽게 삽입, 업데이트 및 삭제를 할 수 있다.
- 테이블 간의 매핑을 만든다.
- 간단한 CLI를 제공한다.
- TypeORM은 간단한 코딩으로 ORM 프레임워크를 사용하기 쉽다.
- TypeORM은 다른 모듈과 쉽게 통합된다.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [],
synchronize: true,
}),
],
})
export class AppModule {}
typeorm은 현재 이런 형태로 nest와 postgresql을 연결한다. 하지만 나는 host, port, username, database
중요한 정보를 그냥 config에 담고 싶지 않았다. 이럴 때는 .env
를 이용하여 환경변수로 뺄 수 있음을 알고 있다. 그래서 나는 환경변수를 이용하여 보안을 강화해보자 한다.
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
console.log('DB_HOST:', process.env.POSTGRE_DB_HOST);
console.log('DB_PORT:', process.env.POSTGRE_DB_PORT);
console.log('DB_USERNAME:', process.env.POSTGRE_DB_USERNAME);
console.log('DB_PASSWORD:', process.env.POSTGRE_DB_PASSWORD);
console.log('DB_DATABASE:', process.env.POSTGRE_DB_DATABASE);
export const typeORMConfig: TypeOrmModuleOptions = {
type: 'postgres',
host: process.env.POSTGRE_DB_HOST,
port: parseInt(process.env.POSTGRE_DB_PORT, 10),
username: process.env.POSTGRE_DB_USERNAME,
password: process.env.POSTGRE_DB_PASSWORD,
database: process.env.POSTGRE_DB_DATABASE,
entities: [__dirname + '/../**/*.entity.{js,ts}'],
synchronize: true,
logging: true,
};
// .env
POSTGRE_DB_HOST='localhost'
POSTGRE_DB_PORT=5432
POSTGRE_DB_USERNAME='ryujiseung'
POSTGRE_DB_PASSWORD='13579asd'
POSTGRE_DB_DATABASE='test'
이런 식으로 작성을 해보았지만, 왠걸.. console.log
를 해보니. undefined 값이 할당되어 있다.. 뭐지...? 뭐가 틀린거지..? 진짜 모를땐 무작정 ChatGPT를 사용하는 것보단 공식 DOCS를 보는 게 훨씬 더 더더더더더ㅓ더 빠르게 해결할 수 있다. 공식 DOCS를 한 번 봐보자
아니 공식문서는 신인가? 누가 gpt가 신이라고 했냐 ㅋㅋㅋㅋ
Configuration
// app.module.ts
import { Module } from '@nestjs/common';
import { BoardModule } from './board/board.module';
import { TypeOrmModule } from '@nestjs/typeorm';
// import { typeORMConfig } from './configs/typeorm.config';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.POSTGRE_DB_HOST,
port: parseInt(process.env.POSTGRE_DB_PORT, 10),
username: process.env.POSTGRE_DB_USERNAME,
password: process.env.POSTGRE_DB_PASSWORD,
database: process.env.POSTGRE_DB_DATABASE,
entities: [__dirname + '/../**/*.entity.{js,ts}'],
synchronize: true,
logging: true,
}),
BoardModule,
],
})
export class AppModule {}
공식문서는 신이다. 그냥 GOAT이다. 공식문서를 보니깐 이렇게 하라고 헀는데 이렇게 하니깐 된다. ^^; 또 다른 접근 방식이 있었는데, 나는 항상 왜 되지
보다는 왜 안 되지?
을 끊임없이 생각한다. 공식문서를 확인해보니. TypeOrmModule이 실행된 후에 환경변수가 설정된다는 사실을 알게되었다. !! 즉, Async를 처리해야한다는 것이다.
// app.module.ts
import { Module } from '@nestjs/common';
import { BoardModule } from './board/board.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeORMConfig } from './configs/typeorm.config';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
useClass: typeORMConfig,
}),
BoardModule,
],
})
export class AppModule {}
// typeorm.config.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
@Injectable()
export class typeORMConfig implements TypeOrmOptionsFactory {
constructor(private readonly configService: ConfigService) {}
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'postgres',
host: this.configService.get<string>('POSTGRE_DB_HOST'),
port: this.configService.get<number>('POSTGRE_DB_PORT'),
username: this.configService.get<string>('POSTGRE_DB_USERNAME'),
password: this.configService.get<string>('POSTGRE_DB_PASSWORD'),
database: this.configService.get<string>('POSTGRE_DB_DATABASE'),
entities: [__dirname + '/../**/*.entity.{js,ts}'],
synchronize: true,
logging: true,
};
}
}
결론적으로 이렇게하 면 된다 ^^ 휴..
schema.prisma
파일에서 model을 통해 스키마를 생성하고 generate를 이용하여 테이블을 생성했다.
model Todo {
id Int @id @default(autoincrement())
todo String
isDone Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
entity를 정의하는데 자주 사용되는 데코레이터
데코레이터 설명 @Entity()
클래스를 데이터베이스 테이블로 매핑 @PrimaryGeneratedColumn()
기본 키 정의 및 자동 증가 @Column()
일반적인 컬럼 정의 @CreateDateColumn()
레코드 생성 시 타임스탬프 자동 기록 @UpdateDateColumn()
레코드 수정 시 타임스탬프 자동 갱신 @DeleteDateColumn()
소프트 삭제를 위한 타임스탬프 기록 @ManyToOne()
다대일(N:1) 관계 정의 @OneToMany()
일대다(1:N) 관계 정의 @ManyToMany()
다대다(N:N) 관계 정의 @JoinColumn()
외래 키 명시적 지정 @JoinTable()
다대다 관계에서 교차 테이블 정의 @OneToOne()
일대일(1:1) 관계 정의
import {
BaseEntity,
Column,
Entity,
// ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
import { BoardStatus } from './board.model';
@Entity()
export class Board extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
@Column()
status: BoardStatus;
// @ManyToOne((type) => User, (user) => user.boards, { eager: false })
// user: User;
}
forRoot vs forRootAsync vs forFeature
- forRoot 동기적으로 전역 설정을 초기화
- forRootAsync 비동기적으로 전역 설정을 초기화
- forRootFeature 모듈별로 특정 엔티티나 리포지토리를 정의
현재는 board 모듈에서 board repository를 정의해야하므로 forRootFeature
를 사용해야한다.
import { Repository } from 'typeorm';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';
import { EBoardStatus } from './board.model';
export class BoardRepository extends Repository<Board> {
async createBoard(createBoardDto: CreateBoardDto): Promise<Board> {
const { title, description } = createBoardDto;
const board = this.create({
title,
description,
status: EBoardStatus.PUBLIC,
});
await this.save(board);
return board;
}
}
``