1. Introduction
NestJS
는 nodejs
위에서 서버 어플리케이션을 구축할 수 있는 프레임워크
이다. 기본적으로 Express
의 API를 사용하고 Fastify
로도 구성할 수 있다.
기존의 Express
는 서버를 빠르게 개발할 수 있었지만 Architecture
라는 중요한 문제점을 해결하지 못했다.
반대로 NestJS
는 Typescript
을 도입하고 객체지향적인 요소들을 사용하고 Dependency Injection
등 패턴을 가져와 개발의 효율성을 높여줄 수 있다. Spring
은 써보지 않았지만 이런 점에서 Spring
과 비슷하다고 하는 것 같다.
Setup
간단히 Nest CLI를 이용하여 프로젝트를 구성할 수 있다.
npm install -g @nestjs/cli
nest new PROJECT
2. Controller
Controller
는 클라이언트의 HTTP 요청을 받아 응답을 리턴해주는 역할 을 한다.
보통 Controller
는 여러개의 라우팅 경로를 가지며 각 경로들은 각각 다른 행동들을 수행한다.
Controller
을 만들기 위해서는 @Controller
라는 데코레이터가 필요하다.
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get(:id)
findOne(@Param('id') id: string) {
return ...
}
}
우선 @Controller
데코레이터로 해당 클래스가 Controller
역할을 하도록 만들고 그 안에 @Get
등 HTTP Method
들을 데코레이터를 이용해 구현할 수 있다. 위의 코드는 /cats/:id
로 들어오는 GET
요청들을 처리하는 라우팅 핸들러인 findOne
함수를 구현한 것이다.
NestJS
의 컨트롤러는 두가지 방식을 통해 클라이언트에 응답을 제공한다. 첫번째는 NestJS
에서 빌트인으로 제공하는 방식으로, 함수가 Object
또는 Array
를 반환하면 NestJS
에서 자체적으로 JSON
형태로 Serialize 해서 응답한다. 반대로 string
, number
, boolean
같은 값들은 Serialize 하지 않고 그냥 응답한다. 이런 방식은 값만 리턴해주고 나머지는 NestJS
에 맡김으로서 편리하게 사용할 수 있다.
두번째 방식으로는 Library-specific 방식으로 사용하는 라이브러리(기본적으로 Express
) 의 Request
, Response
객체를 사용하는 것이다. 해당 방식을 사용하려면 데코레이터를 이용하여, findAll(@Req() req, @Res() res) .. res.status(200).send()
이렇게 직접 Express
의 객체를 이용하여 응답을 구성할 수도 있다. 또한 기본적으로 Controller
는 POST
요청에는 201
, 나머지 요청들에는 200
을 응답한다. 이 부분을 커스텀 하고 싶다면 @HttpCode
데코레이터를 이용하여 변경할 수도 있다.
이외에도 @Body()
@Query()
등 다양한 데코레이터를 사용하여 핸들러를 구성할 수도 있다.
DTO (Data Transfer Object)
POST
요청의 경우 서버는 Body
도 함께 받는 경우가 대부분이다. 이럴 때 Body
가 어떤 오브젝트여야 하는지 미리 정의해 두는 것이 DTO
이다. NestJS
는 자바스크립트의 class
를 이용하여 정의한다.
export class CreateCatDto {
name: string;
...
}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return ...
}
이런 식으로 구성함으로써 Request Body
가 유효한지는 Pipe
에 위임하여 검증할 수 있고, 컨트롤러의 핸들러는 단지 요청을 받아서 응답만 반환해주면 되게 구현할 수 있다.
3. Provider
NestJS
에서의 기초가 되는 개념이 바로 Provider
이다. 대부분의 Service
, Repository
등 대부분의 NestJS
클래스들이 Provider
이다. Provider
는 @Injectable()
데코레이터를 이용한 클래스로, 다른 클래스에 Dependecy
로 등록되거나 다른 Provider
를 Dependecy
로 등록할 수 있다.
위의 사진은 Controller
과의 Dependency
들을 보여준다. NestJS
는 Dependency
들을 SOLID
원칙에 따라 설계하는것을 추천한다고 한다.
위에서의 Controller
는 자신의 역할인 요청을 받고 응답을 하기만 하고, 나머지 부분들은 Service
, Pipe
, Middleware
등 Provider
들을 Dependency
로 받아 처리한다.
Service
Service
는 Controller
에 Dependecy
로 사용되며, 구체적으로 디비에 접근하는 등 요청을 처리하는 역할을 한다.
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
위에서 구현한 CatsService
는 이제 Provider
로써 CatsController
에 Dependecy
로 주입(Injection) 되게 된다.
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
Provider registration
이제 마지막으로 Module
에서 CatsService
가 Provider
로 사용될 수 있도록 등록해 주어야 한다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
imports: [...],
controllers: [CatsController],
providers: [CatsService],
})
export class CatModule {}