Nest.js 탐험 2 - API 작성해보기

2020. 12. 13. 18:07개발/Node & Javascript

728x90
반응형

1. 개요

 이전 글 malgogi-developer.tistory.com/2에서는 Nest.js 개발 환경 설정 및 개략적인 구조를 살펴 보았다.

이번 글에서는 Nest.js를 통해서 실제로 API를 작성해보려고 한다.

로컬에 Mysql을 Docker로 셋업해서 진행해보도록 하자.

 

2. Docker 환경 셋업 및 Mysql 띄우기

 Docker는 컨테이너 기반의 가상화 환경을 제공한다. Docker를 이용하게 될 경우 쉽게 개발환경 셋팅 또는 리얼 환경을 구축할 수 있게 도와준다. 

docs.docker.com/get-docker/ 아래의 사이트에서 로컬에 Docker를 설치해주도록 한다.

그리고 docker-compose도 설치해주도록 한다. docs.docker.com/compose/install/ 

 docker-compose의 경우에는 docker container들을 손쉽게 구성 및 올리거나 내릴 수 있게 도와준다.

만약에 kubernetes 환경을 통해서 구성하고 싶다면 다음 내용은 스킵해도 좋다.

3. Docker 환경 셋업 및 Mysql 띄우기

아래와 같이 docker-compose.yml 파일을 작성해준다. 

version: '3.1'
services:
  db:
    container_name: malgogi-mysql
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: admin
    ports:
      - 3306:3306
  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

 

그리고 다음의 명령어를 통해서 docker container를 띄워준다.

docker-compose up -d;

# status check
docker ps;

이렇게 확인하게 되면 mysql 컨테이너가 띄워진 것을 확인 가능하다.

 

4. Nest.js connection setup

우선 시작하기전 typeorm 과 mysql package를 설치하도록 한다.

npm install --save @nestjs/typeorm typeorm mysql

 

그리고 app.module.ts에서 TypeOrm 관련 설정을 추가해주도록 한다.

@Module({
  imports: [
    TypeOrmModule.forRoot({
      timezone: 'Z',
      type: 'mysql', 
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'admin',
      database: 'malgogi_test',
      retryAttempts: 3,
      autoLoadEntities: true,
      synchronize: true,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})

다음은 몇가지 옵션들을 소개한다.

  • synchronize: 이는 entity field를 업데이트할 경우 자동으로 db에 반영되는지 여부를 선택할 수 있다. 이는 production에서는 끄고 사용하는게 좋다.
  • autoLoadEntities: 코드에 명시된 entity들을 자동으로 스캔해서 사용한다.
  • timezone : DB에 있는 timestamp 또는 date type의 값을 casting할 때 쓰인다. 'local' 또는 'Z' 또는 offset form +HH:MM or -HH:MM. (Default: local)을 사용 할 수 있다.

5. Entity 만들기

일반적으로 Entity의 경우 Database의 Row 또는 하나의 Document의 형태로 대응된다. 즉, 보통 객체를 정의하는 단위로 쓰이는데 Value Object와는 차이가 있다.

 도메인 주도 설계 (www.yes24.com/Product/Goods/5312881) 에서는 Entity와 Value Object의 차이를 가장 크게 식별성에 의해서 구분하고 있다고 정의하고 있다. 예를 들어서 우편 서비스 시스템에서의 주소는 Entity가 될 수 있다.

 하지만 해당 도메인이 Entity 또는 Value Object가 되는 것은 시스템의 정의에 따라서 달라 질 수 있다.

 위의 예제에서의 주소는 Entity가 될 수 있지만 카드 회사에서 주소는 단순히 참조 정보로만 쓰일 수 있다. 이에 따라 Value Object가 될 수 있다.

 

 즉, 객체를 정의함에 있어서 Entity와 Value Object가 존재하고 이를 구별하는 것은 객체의 식별성으로 구분할 수 있다. 하지만 이는 어떠한 시스템을 정의하느냐에 따라서 객체가 Entity 또는 Value Object가 될 수 있음을 주의해야 한다.

 

개념적인 설명은 끝났고, 아래와 같이 Cats.entity.ts 파일을 만들어보자.

 

import {
  Column,
  CreateDateColumn,
  DeleteDateColumn,
  Entity,
  Generated, Index,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';

@Entity()
export class Cat {
  @PrimaryGeneratedColumn()
  idx: number;


  @Column({ unique: true })
  @Generated("uuid")
  id: string;

  @Column()
  name: string;

  @Index("age-idx", { unique: false })
  @Column()
  age: number;

  @Column()
  breed: string;

  @CreateDateColumn({ type: 'timestamp' })
  createdAt!: Date;

  @UpdateDateColumn({ type: 'timestamp' })
  updatedAt!: Date;

  @DeleteDateColumn({ type: 'timestamp' })
  deletedAt?: Date;
}
  • 여기서 date의 경우 timestamp를 명시해주는 이유는 기본적으로 Date type의 column은 datetime의 형태로 저장된다.
  • 그리고 현재 CreateDateColumn, UpdateDateColumn이 있는데, 이는 실제 repository와 연계되어 지정하지 않더라도, 생성, 업데이트 시에 자동으로 생성된다.
  • DeleteDateColumn의 경우 softDelete할 경우 사용된다. 또한 실제로 find를 하게 될 경우 불러오지 않게된다.

그리고 아래는 CreateDto이다. validator를 통해서 validation도 추가로 체크하도록 하자.

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

export class CreateCatDto {
  @IsNotEmpty()
  name: string;

  @IsNumber()
  @IsPositive()
  age: number;

  @IsNotEmpty()
  breed: string;
}

6. Service logic

 

이번 튜토리얼에서는 단순한 CRUD의 형태만 다루려고 한다.

이를 통해서 Cats.service.ts를 작성해보자.

 

import { Injectable } from '@nestjs/common';
import { Cat } from './schemas/cat.entity';
import { CreateCatDto } from './dto/create-cat.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class CatsService {
  constructor(
    @InjectRepository(Cat)
    private catsRepository: Repository<Cat>
  ) {
  }

  async create(saveDto: CreateCatDto): Promise<Cat> {
    const cat = new Cat();
    cat.age = saveDto.age;
    cat.breed = saveDto.breed;
    cat.name = saveDto.name;

    return this.catsRepository.save(cat);
  }

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

  async delete(id: number): Promise<void> {
    await this.catsRepository.softDelete(id);
  }
}

7. Controller logic

 

다음은 controller 쪽이다.

 

import { Body, Controller, Delete, Get, Param, Post, Query } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './schemas/cat.entity';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {
  }

  @Post()
  async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
    return this.catsService.create(createCatDto);
  }

  @Get()
  async getAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  @Delete('/:id')
  async delete(@Param('id') id: number): Promise<void> {
    return this.catsService.delete(id);
  }
}

 

8. 후기

 이번에는 REST API설계를 통해서 개략적으로 어떻게 동작하는지 살펴봤다.

생각보다 정리하는데 시간이 많이 걸려서 간단한 내용만 정의하고,

추가적으로 다음시간에 각각의 내용을 좀 더 깊게 살펴보려고 한다.

 

9. 출처

 

TypeORM - Amazing ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server,

 

typeorm.io

 

NestJS - A progressive Node.js framework

NestJS is a framework for building efficient, scalable Node.js web applications. It uses modern JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reactive Progra

nestjs.com

 

 

 

 

728x90
반응형