컨트롤러(Controller)란 들어오는 요청(request)를 처리하고, 응답(response)를 클라이언트에 보내주는 역할을 한다.
컨트롤러의 목적 : 애플리케이션에 대한 특정 요청을 수신 하는 것
라우팅 메커니즘은 어떤 컨트롤러가 어떤 요청을 수신하는지 제어한다.
기본 컨트롤러를 만들기 위해서 클래스와 데코레이터를 사용한다.
데코레이터는 클래스를 필수 메타데이터와 연결하고, Nest가 라우팅 맵을 만들 수 있도록 한다(요청을 해당 컨트롤러에 연결).
/* cats.controller.ts */
import { Controller, Get } from '@nestjs/common';
// 컨트롤러를 정의하는데 필수인 @Controller() 데코레이터를 사용함
// 라우트 경로(path)를 접두사 cats로 지정
@Controller('cats')
export class CatsController {
// HTTP 요청에 대한 특정 엔드포인트에 대한 핸들러를 생성
@Get()
// 엔드포인트에 GET 요청이 있을 때, findAll() 메서드로 라우팅
findAll(): string {
return 'This action returns all cats';
}
}
CLI를 이용해서 컨트롤러를 만들고 싶다면, 아래 명령을 실행하자.
$ nest g controller cats
Nest에서는 응답을 조작하기 위해 다른 2가지 옵션을 사용한다.
옵션 | 설명 |
---|---|
표준(권장) | 내장 메서드. 요청 핸들러가 JS객체 또는 배열을 반환할때 자동으로 JSON으로 직렬화 된다.(JS의 기본타입을 반환하면 예외처리 됨)상태 코드는 핸들러 수준에서 @HttpCode(...) 데코레이터를 추가하여 쉽게 변경가능하다. |
라이브별(Library-specific) | 메소드 핸들러 시그니처에서 @Res() 데코레이터를 사용하면 삽입할 수 있는 라이브러리별 응답객체를 사용할 수 있다. |
핸들러는 종종 클라이언트 요청 세부정보에 액세스해야한다. Nest는 기본 플랫폼(기본적으로 Express)의 요청객체에 대한 액세스를 제공하며,
/* cats.controller.ts */
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get()
/* 핸들러의 시그니처에 @Req() 데코레이터를 추가하여
* Nest에 주입하도록 지시하여 요청 객체에 액세스 할 수 있음
* request: Request = express 타입을 활용하려면
* @types/express 패키지 설치
*/
findAll(@Req() request: Request): string {
return 'This action returns all cats';
}
}
요청객체는 HTTP 요청을 나타내며 요청 쿼리 문자열, 매개변수, HTTP 헤더 및 본문에 대한 속성을 포함한다. @Body()
or @Query()
와 같은 전용 데코레이터를 대신 사용할 수 있다.
아래 코드는 위에서 정의한 GET라우터로 리소스를 가져오는 엔드포인트 코드에 일반적으로 새 레코드를 생성하는 엔드포인트인 POST핸들러를 추가했다.
/* cats.controller.ts */
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';
}
}
Nest는 모든 표준 HTTP 메소드에 대한 데코레이터를 제공한다.
ex).@Get()
,@Post()
,@Put()
,@Delete()
,@Patch()
,@Options()
,@Head()
,@All()
패턴 기반 라우트도 지원된다.
별표(*)는 와일드카드로 사용되며, 모든 문자조합과 일치한다.
@GET('ab*cd')
findAll() {
return 'This route uses a wildcard';
}
ab*cd
라우트 경로는 abcd, ab_cd, abecd 등과 일치하게 된다. ?, +, *, () 문자는 라우트 경로에 사용될 수 있으며, 하이픈(-)과 점(.)은 문자열 기반 경로로 문자 그대로 해석된다.
201
인 POST 요청을 제외하고 응답 상태 코드는 기본적으로 항상 200
이다. 하지만 핸들러 레벨에서 @nestjs/common 패키지의 @HttpCode() 데코레이터를 추가하여 변경가능하다.
@Post()
@HttpCode(204)
create() {
return 'This action adds a new cat';
}
커스텀 응답헤더를 지정하기 위해서 @nestjs/common
패키지의 @Header()데코레이터 또는 res.header()를 직접호출하는 라이브러리별 응답객체를 사용한다.
@Post()
@Header('Cache-Control', 'none')
create() {
return 'This action adds a new cat';
}
@Redirect
데코레이터를 사용하면 응답을 특정 URL로 리다이렉션 할 수 있다.
@Get()
@Redirect('https://nestjs.com', 301)
동적 데이터를 수락해야하는 경우 정적 경로가 있는 라우트는 작동하지 않는다. 아래는 @Get()
데코레이터를 이용한 경로 매개변수 토큰 사용법이다.
@Get(':id')
findOne(@Param() params): string {
console.log(params.id);
return 'This action returns a #${params.id} cat';
}
@Controller
데코레이터는 들어오는 요청의 HTTP 호스트가 특정값과 일치하도록 host
옵션을 사용할 수 있다.
이 때, host 옵션은 토큰을 사용하여 호스트 이름의 해당 위치에서 동적값을 캡처할 수 있다. 이런 방식으로 선언된 호스트 매개변수는 @HostParam()
데코레이터를 사용하여 액세스 할 수 있으며 메소드 서명에 추가해야한다.
@Controller({ host: ':account.example.com' })
export class AccountController {
@Get()
getInfo(@HostParam('account') account: string) {
return account;
}
}
모든 비동기 함수는 Promise를 반환해야하는데, Nest 라우트 핸들러는 RxJS 관찰가능한 스트림을 반환할 수 있다. 이 때는 Promise 대신 Observable을 사용한다.
TypeScript를 사용하는 경우, DTO 스키마를 결정해야 한다. DTO는 데이터가 네트워크를 통해 전송되는 방식을 정의하는 객체이다. TypeScript 인터페이스를 사용하기 보다 class를 사용하여 DTO 스키마를 정의하는 것이 좋다. 클래스는 javascript ES6표준의 일부이므로 컴파일된 자바스크립트에서 실제 엔티티로 유지되지만, TypeScript 인터페이스는 트랜스 파일중에 제거되기 때문에 Nest는 런타임에 이를 참조할 수 없기때문이다.
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
컨트롤러는 항상 모듈에 속하므로 @Module()
데코레이터 내에 controllers 배열을 포함한다. @Module()
데코레이터를 사용하여 모듈 클래스에 메타데이터를 첨부하면, Nest는 이제 어떤 컨트롤러를 마운트해야하는지 쉽게 반영할 수 있다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class AppModule {}
응답을 조작하는 또 다른 방법은 라이브러리별 응답객체를 사용하는 것이다. 특정 응답객체를 삽입하려면 @Res()
데코레이터를 사용해야한다.
@Res()
데코레이터를 사용했을 때, 응답객체(헤더 조작, 라이브러리별 기능등)에 대한 전체 제어를 제공함으로써 더 많은 유연성을 허용한다. 하지만, 코드가 플랫폼에 종속되고 테스트가 어렵다는 것이다.