똑같은 삽질은 2번 하지 말자

NestJS vol.3 (TypeORM) 본문

카테고리 없음

NestJS vol.3 (TypeORM)

곽빵 2023. 2. 5. 15:53

개요

NestJS를 배우면서 남기는 기록

TypeORM이란?

Typescript와 Javascript를 바탕으로 ORM을 구현한 친구이다.

(ORM은 간단하게 말해서 DB의 Table을 Javascript의 class를 만들어서 그걸 그대로 Table에 매핑하는 기술이다)

TypeORM의 특징

  • 데코레이터를 이용해 클래스와 속성에 메타데이터를 추가해서 객체와 테이블 간의 매핑을 설정할 수 있다.
  • 리포지토리 패턴으로 코드를 작성할 수 있어서 데이터베이스와 관련된 작업을 캡슐화 할 수 있다.
  • 마이그레이션 지원 (이력을 파일로 남길 수 있다.)
  • Active Record & Data Mapper 두가지 패턴을 모두 지원한다.
    • Active Record는 엔티티가 자신의 데이터베이스의 작업을 처리하는 패턴
    • Data Mapper는 엔티티가 데이터 구조와 비즈니스 로직만 담고, 데이터베이스의 작업처리는 별도의 리포지토리나 서비스를 통해 수행하는 패턴

일단 코드로 시작해보자

우선 postgreSQL의 데이터베이스를 도커 컨테이너로 한개 만들자

version: '3'

services:
  postgres-db:
    image: postgres:14
    container_name: postgres-db
    environment:
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test
      - POSTGRES_DB=playground-nestjs
    ports:
      - '5432:5432'
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

 

패키지 설치

npm install @nestjs/typeorm typeorm pg

 

app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PostModule } from './post/post.module';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    PostModule,
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT || '5432', 10),
      username: process.env.DB_USERNAME || 'test',
      password: process.env.DB_PASSWORD || 'test',
      database: process.env.DB_DATABASE || 'playground-nestjs',
      autoLoadEntities: true, // 엔티티 파일(*.entity.ts) 자동 로드
      synchronize: true, // 엔티티 파일 변경 시 테이블 자동 동기화
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

TypeOrmModule이라는 모듈을 imports에 추가 해준다.

  • autoLoadEntities는 엔티티 파일을 자동으로 로드해주는 옵션
  • synchronize라는 옵션은 내가 entities에 추가한 친구들을 실제 테이블이랑 동기화하는 옵션
  • logging은 ORM에서 실제로 생성해내는 쿼리들의 로그를 남기는 옵션

post.entity.ts 작성

import { Column, PrimaryGeneratedColumn } from 'typeorm';

import { Entity } from 'typeorm';

@Entity({
  name: 'posts',
})
export class Post {
  @PrimaryGeneratedColumn({
    type: 'int',
  })
  id: number;

  @Column({
    type: 'varchar',
    length: 255,
    nullable: false,
  })
  title: string;

  @Column({
    type: 'text',
    nullable: false,
  })
  content: string;

  @Column({
    type: 'int',
    name: 'author_id',
    nullable: false,
  })
  authorId: number;
}
  • 각각의 데코레이션에 대해서는 직관적이니 설명은 생략
  • 이 시점에서 서버를 재가동하면 데이터베이스에 테이블이 생성되어있다. (synchronize옵션에 의해)

 

post.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PostController } from './post.controller';
import { PostService } from './post.service';
import { Post } from './post.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Post])],
  controllers: [PostController],
  providers: [PostService],
})
export class PostModule {}
  • 모듈에도 TypeORM을 사용한다라는 코드를 추가

 

post.service.ts

import { Injectable } from '@nestjs/common';
import { Post } from './post.entity';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class PostService {
  constructor(
    @InjectRepository(Post)
    private readonly postRepository: Repository<Post>,
  ) {}

  async findAll(): Promise<Post[]> {
    return this.postRepository.find();
  }

  async findOne(id: number): Promise<Post | null> {
    return this.postRepository.findOne({ where: { id } });
  }

  async create(createPostDto: CreatePostDto): Promise<Post> {
    const post = this.postRepository.create(createPostDto);
    return this.postRepository.save(post);
  }

  async update(id: number, updatePostDto: UpdatePostDto): Promise<Post | null> {
    await this.postRepository.update(id, updatePostDto);
    return this.postRepository.findOne({ where: { id } });
  }

  async remove(id: number): Promise<void> {
    await this.postRepository.delete(id);
  }
}
Comments