이 실습 프로젝트에서는 NestJS를 사용하여 포스트 기능을 구현하는 방법을 단계별로 살펴보겠습니다. 우리는 Controller 구현, Service 구현, DTO 정의, Entity 모델 정의, 그리고 Validation 구현의 다섯 가지 주요 부분을 다룰 것입니다.

npm i --save class-validator class-transformer

0. 강의에서 작성한 코드

https://github.com/lopun-lecture/first-nestjs-project/commit/c82e7ddff784efe1c1f8f8e762332ae14a81b7df

1. 포스트 Entity 모델 정의

먼저 Post Entity 모델을 post.entity.ts 파일에 정의합니다:

export class Post {
  id: number;
  title: string;
  content: string;
  authorId: number;
}

2. DTO 정의

다음으로 DTO(Data Transfer Object)를 정의합니다. create-post.dto.ts 파일을 생성합니다:

import { IsNotEmpty, IsString, IsNumber } from 'class-validator';

export class CreatePostDto {
  @IsNotEmpty()
  @IsString()
  title: string;

  @IsNotEmpty()
  @IsString()
  content: string;

  @IsNotEmpty()
  @IsNumber()
  authorId: number;
}

update-post.dto.ts 파일도 생성합니다:

import { IsOptional, IsString, IsNumber } from 'class-validator';

export class UpdatePostDto {
  @IsOptional()
  @IsString()
  title?: string;

  @IsOptional()
  @IsString()
  content?: string;

  @IsOptional()
  @IsNumber()
  authorId?: number;
}

3. 포스트 레포지토리 구현 (임시 코드. TypeORM 수업 후 제거예정)

post.repository.ts

// AS-IS

import { Injectable } from '@nestjs/common';
import { Post } from './post.entity';

@Injectable()
export class PostRepository {
  private posts: Post[] = [];
  private idCounter = 1;

  find(): Post[] {
    return this.posts;
  }

  findOne({ where: { id } }: { where: { id: number } }): Post | null {
    return this.posts.find((post) => post.id === id) || null;
  }

  create(model): Post {
    const newPost: Post = { id: this.idCounter++, ...model };
    this.posts.push(newPost);
    return newPost;
  }

  save(post: Post): Post {
    const index = this.posts.findIndex((p) => p.id === post.id);
    if (index !== -1) {
      this.posts[index] = post;
    } else {
      this.posts.push(post);
    }
    return post;
  }

  update(id: number, model): void {
    const index = this.posts.findIndex((post) => post.id === id);
    if (index !== -1) {
      this.posts[index] = { ...this.posts[index], ...model };
    }
  }

  delete(id: number): void {
    this.posts = this.posts.filter((post) => post.id !== id);
  }
}

// TO-BE (타입 에러 제거)