똑같은 삽질은 2번 하지 말자
NestJS vol.3 (TypeORM) 본문
개요
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);
}
}
- 서비스 계층에서는 이런 방식으로 TypeORM을 활용할 수 있다.
- repository의 메소드에 관해서는 이하의 공식문서를 참조
Comments