import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
NestJS의 Controller는 Express에서 Router와 비슷한 역할을 해준다. 엔드포인트 요청에 대한 처리를 조작해주는데, @Controller 데코레이터 클래스를 통해 Route될 엔드포인트를 설정해준다. 이후 constructor를 통해 Provider를 정의해 준다.
Controller의 메소드에도 각각 데코레이터를 달아 모든 HTTP Request(@Post()
, @Put()
, @Delete()
, @Patch()
, @Options()
, @Head()
, @All()
) 요청에 대한 응답을 메소드를 통해 적절한 동작을 지정해 줄수 있다. 그 외에도 데코레이션을 추가해 Redirection, Header, HttpCode등의 부가적인 기능을 지정할 수도 있다.
또한 각 메소드의 인자에도 데코레이터를 통해 Request Object
를 할당할 수 있으며 Paramiter
나 Query
등을 한번에 받아올 수도 있다.
@Controller()
데코레이터를 통해 설정해 엔드포인트를 지정하여 라우팅 할 수 있으며 controller.ts
파일 안에 필수적으로 포함되어 있어야 한다. /cats
엔드포인트에 라우팅하는 예제를 통해 살펴보자.
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
// '/cats/' 엔드포인트로 접속
export class CatsController {
@Get()
// HTTP GET 요청에 대한 응답을 지정
findAll(): string {
return 'This action returns all cats';
}
}
$ nest g controller cats
CLI 명령을 통해 생성해 줄 수도 있다.
/cats
엔드포인트에 get
요청에 대한 응답까지 지정해주는 코드이다. 클래스 선언과 메소드 선언부에 데코레이터를 이용해서 간단하게 지정해 줄 수 있다. typescript
를 활용하기 위해서는 응답에 대한 타입을 지정해야 한다. 응답을 반환하는 방법은 두가지인데, 위의 예제처럼 메소드의 return
값을 통해 응답하거나, Express
에서 사용하던 방법대로 res
객체를 통해 응답하는 방법이 있다. NestJS
에서는 기본적으로 메소드의 return
을 통해 응답하는 방법을 추천한다. 이 방법을 사용하면 기본적으로 status code
는 200
으로 지정되며 POST
요청시에 201
을 사용한다. 두가지 방법을 혼용하는 것은 불가능하다.
HTTP Request에 대한 엔드포인트 지정도 가능하다. 만약 @Get
요청에 대한 엔드포인트를 hello
로 지정한다면, findAll
메소드는 /cats/hello
에서 GET 요청을 받는다.
HTTP Request
를 수행하기 위해서는 Request
객체에 접근해야 하는 경우가 생긴다. 이 경우에는 인자에 @Req
(혹은 @Request
) 데코레이터를 붙여 Request 객체를 지정해 사용할 수 있다. NestJS에서는 @Param
, @Body
등 Requset
의 내부 객체도 한번에 받아올 수 있는 데코레이터를 제공한다 물론 req
객체에 직접 접근하여 사용할 수도 있다.
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get()
findAll(@Req() request: Request): string {
//@Req() 데코레이터를 통해 request 객체를 인자로 가져온다.
//인터페이스를 지정할 때는 express모듈에서 Request를 가져와 사용한다.
return 'This action returns all cats';
}
}
여기서 @Response()
데코레이터를 통해 res
객체를 불러올 경우 라우팅에서 설명한 NestJS
의 응답방법은 사용이 불가능해지고 기존 Express
에서 res
객체를 조작하는 방식으로 작동하게 된다. 호환성을 위해 존재하는 옵션이니 일반적인 상황에서는 사용을 지양하는 것이 좋다.
기본적인 HTTP 메소드에 해당하는 데코레이터(@Post()
, @Put()
, @Delete()
, @Patch()
, @Options()
, @Head()
, @All()
)를 모두 클래스 메소드 위에 붙여 요청에 대한 작업을 지정해 줄 수 있으며 각 HTTP 의 요청 엔드포인트를 데코레이터를 호출할 때 지정해 줄 수도 있다.
import { Controller, Get, Post } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Post()
//POST 요청에 대한 동작을 추가.
create(): string {
return 'This action adds a new cat';
}
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
모든 엔드포인트에는 와일드카드를 지정해 줄 수도 있다.
@Get('ab*cd')
//ab*cd에 대한 요청을 모두 수행.
findAll() {
return 'This route uses a wildcard';
}
@HttpCode()
데코레이터를 추가해 요청에 대한 statud code를 임의로 지정해 줄수도 있다.
@Post()
@HttpCode(204)
create() {
return 'This action adds a new cat';
}
다만, 상황에 따라 다른 status code
를 지정해 줘야할 때는 @Res()
데코레이터를 통해 res
객체를 조작하거나 throw
를 이용해 조작할 수 있다.
임의로 응답헤더를 지정해 줄 수도 있다.
@Post()
@Header('Cache-Control', 'none')
create() {
return 'This action adds a new cat';
}
리다이렉션을 사용할 수도 있는데, 리다이렉션될 링크와 status code를 인수로 받는다. status code는 기본적으로 302로 지정된다. @Redirect 데코레이터의 인자를 동적으로 지정하려면 메소드의 return 값을 통해 동적으로 지정해 줄 수 도 있다.
{
"url": string,
"statusCode": number
}
@Get("docs")
@Redirect("https://docs.nestjs.com", 302)
//기본적인 Redirect 속성
getDocs(@Query("version") version) {
if (version && version === "7") {
return {url: "https://docs.nestjs.com/v5/"};
//메소드를 통해 특정 조건에서 속성을 재설정할 수도 있다.
}
}
엔드포인트를 통해 파라미터를 받아올수도 있다. :string
형식으로 지정해 주면 해당 자리에 들어오는 값을 파라미터로 전달하여 params객체로 받아올 수 있다.
@Get(':id')
//id 파라미터를 받는다.
findOne(@Param() params): string {
// 데코레이터를 통해 파라미터 객체 지정
console.log(params.id);
return `This action returns a #${params.id} cat`;
}
@Controller
데코레이터를 통해 서브도메인을 라우팅 해줄 수도 있다.
@Controller({ host: 'admin.example.com' })
// host 속성을 통해 서브도메인을 지정해준다.
export class AdminController {
@Get()
index(): string {
return 'Admin page';
}
}
파라미터를 통해 서브도메인 지정도 가능하다. 이 경우에는 @HostParam
데코레이터를 통해 서브도메인 파라미터의 이름을 지정해 줘야 한다.
@Controller({ host: ':account.example.com' })
export class AccountController {
@Get()
getInfo(@HostParam('account') account: string) {
return account;
}
}
모든 메소드는 async
/await
을 통해 비동기 처리를 할 수 있다. 대부분의 ORM은 비동기로 처리되기 때문에 자주 사용하게 된다. 비동기 메소드는 Promise
객체를 반환한다.
@Get()
async findAll(): Promise<any[]> {
return [];
}
RxJS
를 사용하는 경우 Observable
객체를 반환할 수도 있다.
@Post
데코레이터를 통해 POST
요청을 받으면 함께 Body
객체도 받을 수 있는데, 먼저 @Body
데코레이터를 통해 Body 객체를 지정해줄 수 있다. 그 전에 먼저 *.dts.ts 파일을 생성해 dts(Data Transfer Object)의 타입을 클래스로 정의한다. typescript
의 인터페이스가 아니라 클래스로 지정하는 이유는, 인터페이스의 경우는 TSC에서만 작동하는 실체가 없는 객체이지만 클래스는 TSC
와 상관없이 ES6
에서 기본적으로 지원하는 자바스크립트 문법이기 때문에 컴파일 이후에도 계속 실체할 수 있다.
CreateCatDto
클래스를 정의해보자:
//create-cat.dto.ts
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
이제 controller
에 메소드의 인수로 body
객체를 정의할 수 있다.
@Post()
async create(@Body() createCatDto: CreateCatDto) {
//@Body()데코레이터를 통해 createCatDto를 body객체로 지정하고 CreateCatDto 타입을 지정한다.
return 'This action adds a new cat';
}
NestJS
에서는 별도의 BodyParse
가 필요없다.
Controller
의 다양한 기능들을 사용하여 아래와 같은 컨트롤러를 만들 수 있다.
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`;
}
}
컨트롤러를 모두 정의했다면 CatsController
를 모듈의 controllers
에 넘겨줘야 제대로 라우팅될 수 있다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class AppModule {}
이렇게 AppModule
에 CatsController
를 붙여주면 main.ts
에서 AppModule
을 통해 app
객체를 생성하고 localhost
를 통해 서비스된다.
이렇게 지정된 컨트롤러의 메소드는 보통 Provider
를 통해 전달받은 메소드를 작동하는 식으로 구현된다. 컨트롤러 내부에서 작동시킬수도 있지만, 좀더 구조화되고 체계적으로 NestJS
를 사용하기 위해서 별도의 Provider
를 구현하여 구성해 보자