컨트롤러의 목적은 애플리케이션을 위한 특정 요청들을 받는 것이다.
클라이언트로부터 들어온 HTTP 요청들은 라우팅 메커니즘에 따라서 알맞은 컨트롤러로 분배된다.
Nest에서는 @Controller, @Get, @Post 등 데코레이터를 이용해서 컨트롤러를 구성할 수 있다.
nestcli를 이용해서 CRUD 컨트롤러를 간단히 만들 수 있다.
$ nest g resource [name]
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
@Controller, @Get 데코레이터를 사용하는 예제 코드
@Controller 데코레이터는 관계있는 라우터를 그룹화하는데 사용한다. 데코레이터의 인자로 라우팅 주소를 받는다.
@Controller('cats')라고 작성하면 /cats 주소로 접근하는 모든 요청은 해당 컨트롤러를 거쳐갈 것이다.
Express.js로 따지자면 아래와 같이 만들 수 있다.
app.use('cats', CatsController);
@Get 데코레이터는 get메소드로 들어온 HTTP 요청을 받는 핸들러를 의미한다.
@Controller 데코레이터와 마찬가지로 인자로 라우팅 주소를 받는다. 인자로 아무 값도 넘기지 않으면 '/'로 연결된다.
즉, @Controller('cats') 내부의 @Get()는 /cats 경로로 GET 메소드 HTTP 요청이 들어오는 경우에 실행하는 핸들러가 된다.
만약 @Get('/profile') 애노테이션으로 라우터를 만든다면 /cats/profile 경로로 들어온 HTTP 요청을 처리할 것이다.
기본적으로 Nest 컨트롤러는 상태코드 200을 반환한다.
Nest는 두 종류의 response 옵션을 제공하는데 아래와 같다.
Standard (권장)
핸들러가 객체 또는 배열인 경우에 자동으로 이를 JSON으로 직렬화한다.
원시타입(string, number...)인 경우에는 직렬화하지 않고 보낸다.
상태코드는 항상 기본 200이다. (POST의 경우 201)
상태코드는 @HttpCode 데코레이터로 설정할 수 있다.
Library-specific
특정 라이브러리의 response 객체를 의미함. (e.g. Express)
이러한 객체는 @Res 데코레이터를 사용해서 호출한다.
Nest에서도 @Res(), @Next() 등을 호출해서 사용할 수 있다.
이 데코레이터들을 호출하면 standard 옵션은 자동으로 비활성화 된다.
두 옵션을 동시에 사용하려면 @Res({ passthrough: true }) 이렇게 passthrough 옵션을 true로 설정하면 된다.
핸들러가 클라이언트의 request 객체에 접근할 일이 종종있다.
Nest는 request 객체에 접근할 수 있는 @Req 데코레이터를 지원한다.
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get()
findAll(@Req() request: Request): string {
return 'This action returns all cats';
}
}
@Req 데코레이터 사용 예시
request 객체 전체가 아니라 클라이언트로부터 전달받은 body나 querystring만 필요한 경우에도 @Body, @Query 데코레이터를 위와 같이 호출하여 사용할 수 있다.
Nest는 모든 기본 HTTP 메소드를 데코레이터로 제공한다. 이 데코레이터들은 '@nestjs/common' 모듈에 선언되어 있다.
@Get, @Post, @Put, @Delete, @Patch, @Options, @Head가 있으며 추가로 @All 데코레이터로 이 모든 메소드를 핸들링하는 라우터를 만들 수 있다.
import { Controller, Get, Post } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Post()
create(): string {
return 'This action adds a new cat';
}
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
POST 메소드 라우팅 예시
라우트 주소에 와일드카드 문자를 포함할 수 있다.
@Get('ab*cd')
findAll() {
return 'This route uses a wildcard';
}
여기서 'abcd' 라우트 주소는 'abcd', 'ab_cd', 'abecd' 등과 매치된다.
?, +, , (, ) 문자를 라우트 주소에 사용할 수 있고 정규표현식에서의 사용법을 일부 포함한다.
Nest는 async / await과 RxJS의 observable streams을 지원한다.
async 함수는 반드시 Promise 타입을 반환한다.
// async 함수
@Get()
async findAll(): Promise<any[]> {
return [];
}
// observable streams 사용
@Get()
findAll(): Observable<any[]> {
return of([]);
}
위에서 살펴본 POST 메소드 예시 코드를 보면 핸들러가 클라이언트로부터 아무런 값도 받지 않고 있다.
@Body 애노테이션을 이용해서 클라이언트로부터 request body를 받아보자.
class CreateCatDto {
name: string;
age: number;
breed: string;
}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}
ValidationPipe 기능을 사용해서 클라이언트로부터 받은 값들이 유효한지 검사할 수 있다.
import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';
@Controller('cats')
export class CatsController {
@Post()
create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}
@Get()
findAll(@Query() query: ListAllEntities) {
return `This action returns all cats (limit: ${query.limit} items)`;
}
@Get(':id')
findOne(@Param('id') id: string) {
return `This action returns a #${id} cat`;
}
@Put(':id')
update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
return `This action updates a #${id} cat`;
}
@Delete(':id')
remove(@Param('id') id: string) {
return `This action removes a #${id} cat`;
}
}
기본적인 CRUD 컨트롤러 구성
이렇게 만든 컨트롤러를 Nest가 인식할 수 있게 root module에 추가해주어야 한다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class AppModule {}