postgreSQL랑 친한 척 하기

류지승·2024년 9월 18일
0

nestjs

목록 보기
6/10
post-thumbnail

기존에 DBMysql를 사용했었다. 대학교 때, 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(Object Relational Mapping)

TypeORM이란 nodejs에서 실행되고 Typescript로 작성된 객체 관계형 매퍼 라이브러리이다.

TypeORM 특징과 이점

  • 모델을 기반으로 데이터베이스 테이블 체계를 자동으로 생성
  • 데이터베이스에서 객체를 쉽게 삽입, 업데이트 및 삭제를 할 수 있다.
  • 테이블 간의 매핑을 만든다.
  • 간단한 CLI를 제공한다.
  • TypeORM은 간단한 코딩으로 ORM 프레임워크를 사용하기 쉽다.
  • TypeORM은 다른 모듈과 쉽게 통합된다.

두번째 벽 .env

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 를 이용하여 환경변수로 뺄 수 있음을 알고 있다. 그래서 나는 환경변수를 이용하여 보안을 강화해보자 한다.

그냥 원래 했던 .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를 한 번 봐보자

공식문서를 읽는 힘, Configuartion

아니 공식문서는 신인가? 누가 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를 처리해야한다는 것이다.

DataBase-Async 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.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,
        };
    }
}

결론적으로 이렇게하 면 된다 ^^ 휴..

Entity 생성

prisma에서는 TABLE을 어떻게 생성했나?

schema.prisma 파일에서 model을 통해 스키마를 생성하고 generate를 이용하여 테이블을 생성했다.

model Todo {
  id    Int     @id @default(autoincrement())
  todo String  
  isDone  Boolean @default(false)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

TypeORM은 TABLE을 어떻게 생성하는가?

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;
    }
}
``
profile
성실(誠實)한 사람만이 목표를 성실(成實)한다

0개의 댓글

관련 채용 정보