2. Controllers
컨트롤러는 들어오는 요청을 처리하고 클라이언트에 응답을 반환하는 역할을 한다.
컨트롤러의 목적은 애플리케이션에 대한 특정 요청을 수신하는 것이다. 라우팅 매커니즘은 어떤 컨트롤러가 어떤 요청을 수신하는지 제어한다. 종종 각 컨트롤러에는 둘 이상의 라우트가 있고 다른 라우트는 다른 작업을 수행할 수 있다.
기본 컨트롤러를 만들기 위해 클래스와 데코레이터를 사용한다. 데코레이터는 클래스를 필수 메타데이터와 연결하고 Nest가 라우팅 맵을 생성할 수 있도록 한다.
1. Routing
다음 예제에서는 기본 컨트롤러를 정의하는 데 필요한 @Controller()
데코레이터를 사용한다. cats
의 라우트 경로(path) 접두사를 지정한다. @Controller()
데코레이터에서 경로 접두사를 사용하면 관련 라우트 집합을 쉽게 그룹화하고 반복적인 코드를 최소화할 수 있다. 예를 들어 라우트 /customers
아래에서 고객 엔터티와의 상호 작용을 관리하는 라우트 집합을 그룹화하도록 선택할 수 있다. 이 경우 파일의 각 경로에 대해 경로의 해당 부분을 반복할 필요가 없도록 @Controller()
데코레이터에서 경로 접두사 customers
를 지정할 수 있다.
import { Controller, Get } from "@nestjs/common";
@Controller("cats")
export class CatsController {
@Get()
findAll(): string {
return "This action returns all cats";
}
}
Note
CLI를 사용하여 컨트롤러를 생성하려면 $ nest g controller cats
명령을 실행하면 된다.
findAll()
메서드 앞의 @Get()
HTTP 요청 메서드 데코레이터는 Nest에 HTTP 요청에 대한 특정 엔드포인트에 대한 핸들러를 생성하도록 지시한다. 엔드포인트는 HTTP 요청 메서드(이 경우 GET) 및 라우트 경로에 해당한다. 핸들러의 라우트 경로는 컨트롤러에 대해 선언된 접두사와 메서드의 데코레이터에 지정된 경로를 연결하여 결정된다. 모든 라우트(cats
)에 대해 접두사를 선언했고 데코레이터에 경로 정보를 추가하지 않았으므로 Nest는 GET /cat
요청을 이 핸들러에 매핑한다. 언급했듯이 경로에는 컨트롤러 경로 접두사와 요청 메서드 데코레이터에 선언된 모든 경로 문자열이 포함된다. 예를 들어, 데코레이터 @Get('profile')
과 결합된 customers
의 경로 접두사는 GET /customers/profile
과 같은 요청에 대한 라우트 매핑을 생성한다.
위의 예제에서 이 엔드포인트에 GET 요청이 발생하면 Nest는 요청을 findAll()
메서드로 라우팅한다.
2. Request object
핸들러는 종종 클라이언트 요청 세부 정보에 액세스해야 한다. Nest는 기본 플랫폼(기본적으로 Express)의 요청 객체에 대한 액세스를 제공한다. 핸들러의 시그니처에 @Req()
데코레이터를 추가하여 요청 객체를 삽입하도록 Nest에 지시하여 요청 객체에 액세스할 수 있다.
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";
}
}
요청 객체는 HTTP 요청을 나타내며 요청 쿼리 문자열, 매개변수, HTTP 헤더 및 바디에 대한 속성을 가지고 있다. 대부분의 경우 이러한 속성을 수동으로 가져올 필요가 없으며, 대신 바로 사용할 수 있는 @Body()
또는 @Query()
와 같은 전용 테코레이터를 사용할 수 있다. 다음은 제공되는 데코레이터와 이들이 나타내는 일반 플랫폼별 객체 목록이다.
데코레이터 | 객체 목록 |
---|---|
@Request() , @Req() |
req |
@Response() , @Res() |
res |
@Next() |
next |
@Session() |
req.session |
@Param(key?: string) |
req.params / req.params[key] |
@Body(key?: string) |
req.body / req.body[key] |
@Query(key?: string) |
req.query / req.query[key] |
@Headers(name?: string) |
req.headers / req.headers[name] |
@Ip() |
req.ip |
@HostParam() |
req.hosts |
3. Resources
앞서 cats
리소스(GET 라우트)를 가져오기 위해 엔드포인트를 정의했다. 여기에 더해 새 레코드(데이터베이스)를 생성할 수 있는 POST 핸들러를 생성한다.
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는 @Get()
, @Post()
, @Put()
, @Delete()
, @Patch()
, @Options()
, @Head()
와 같은 모든 표준 HTTP 메서드에 대한 데코레이터를 제공한다. 또한 @All()
은 모든 것을 처리하는 엔드포인트를 정의한다.
4. Route wildcards
패턴 기반 라우트도 지원된다. 예를 들어 애스터리스크는 와일드카드로 사용되며 모든 문자 조합과 일치한다.
'ab*cd'
라우트 경로는 abcd
, ab_cd
, abecd
등과 일치한다. ?
, +
, *
, ()
문자는 라우트 경로에 사용될 수 있다.
5. Status code
언급했듯이 응답 상태 코드는 201
인 POST 요청을 제외하고 기본적으로 항상 200
이다. 핸들러 레벨에서 @HttpCode(…)
데코레이터를 추가하여 쉽게 변경할 수 있다.
Note
@nestjs/common
패키지에서 HttpCode
를 임포트한다.
6. Headers
사용자 정의 응답 헤더를 지정하려면 @Header()
데코레이터 또는 응답 객체를 사용하여 res.header()
를 직접 호출할 수 있다.
Note
@nestjs/common
패키지에서 Header
를 임포트한다.
7. Redirection
응답을 특정 URL로 리다이렉션하려면 Redirect()
데코레이터 또는 응답 객체를 사용하여 res.redirect()
를 직접 호출할 수 있다. @Redirect()
는 url
과 statusCode
라는 두 개의 인수를 사용하며 둘 다 옵셔널이다. 생략하면 statusCode
의 기본값은 302
(Found
)이다.
때로는 HTTP 상태 코드 또는 리다이렉션 URL을 동적으로 결정해야 하는 경우도 있다. 다음과 같은 형태를 가진 라우트 핸들러에서 객체를 반환하여 이 작업을 수행한다.
반환된 값은 @Redirect()
데코레이터에 전달된 모든 인수를 재정의한다.
@Get('docs')
@Redirect('https://docs.nestjs.com', 302)
getDocs(@Query('version') version) {
if (version && version === '5') {
return { url: 'https://docs.nestjs.com/v5/' };
}
}
8. Route parameters
정적 경로가 있는 라우트는 요청의 일부로 동적 데이터를 수락해야 하는 경우 작동하지 않는다(예: id
가 1
인 고양이를 가져오기 위해 GET /cats/1
에 요청). 매개변수가 있는 라우트를 정의하기 위해 라우트 경로에 라우트 매개변수 토큰을 추가하여 요청 URL의 해당 위치에서 동적 값을 캡처할 수 있다. 아래 @Get()
데코레이터 예제의 라우트 매개변수 토큰은 이 사용법을 보여준다. 이러한 방식으로 선언된 라우트 매개변수는 @Param()
데코레이터를 사용하여 액세스할 수 있으며, 이는 메서드 시그니처에 추가되어야 한다.
@Get(':id')
findOne(@Param() params): string {
console.log(params.id);
return `This action returns a #${params.id} cat`;
}
위의 코드에서 볼 수 있듯이 params.id
를 참조하여 id
매개변수에 액세스할 수 있다. 특정 매개변수 토큰을 데코레이터에 전달한 다음 메서드 바디에서 이름으로 직접 라우트 매개변수를 참조할 수도 있다.
9. Sub-Domain Routing
@Controller
데코레이터는 들어오는 요청의 HTTP 호스트가 특정 값과 일치하도록 요구하는 호스트 옵션을 사용할 수 있다.
@Controller({ host: "admin.example.com" })
export class AdminController {
@Get()
index(): string {
return "Admin page";
}
}
라우트 경로와 유사하게 호스트 옵션은 토큰을 사용하여 호스트 이름의 해당 위치에서 동적 값을 캡처할 수 있다. 아래 @Controller()
데코레이터 예제의 호스트 매개변수 토큰은 이 사용법을 보여준다. 이러한 방식으로 선언된 호스트 매개변수는 @HostParam()
데코레이터를 사용하여 액세스할 수 있으며 이는 메서드 시그니처에 추가되어야 한다.
@Controller({ host: ":account.example.com" })
export class AccountController {
@Get()
getInfo(@HostParam("account") account: string) {
return account;
}
}
10. Asynchronicity
모든 비동기 함수는 Promise
를 반환해야 한다. Nest는 자체적으로 리졸브(resolve)할 수 있는 값을 반환할 수 있다.
또한 Nest 라우트 핸들러는 RxJS 옵저버블 스트림을 반환할 수 있다.
11. Request payloads
POST 라우트 핸들러의 이전 예제에서는 클라이언트 매개변수를 허용하지 않았다. 여기에 @Body()
데코레이터를 추가하여 이 문제를 해결해 보자.
그러나 먼저 DTO(Data Transfer Object) 스키마를 결정해야 한다. DTO는 데이터가 네트워크를 통해 전송되는 방법을 정의하는 객체이다. TypeScript 인터페이스를 사용하거나 간단한 클래스를 사용하여 DTO 스키마를 결정할 수 있다.
CreateCatDto
클래스를 생성해 보자.
CatsController
내에서 새로 생성된 DTO를 사용할 수 있다.
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}
12. Full resource sample
다음은 사용 가능한 여러 데코레이터를 사용하여 기번 컨트롤러를 만드는 예제이다. 이 컨트롤러는 내부 데이터에 액세스하고 조작하는 몇 가지 방법을 제공한다.
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`;
}
}
13. Getting up and running
위의 컨트롤러가 완전히 정의된 상태임에도 불구하고, Nest는 여전히 CatsController
가 존재한다는 사실을 알지 못한다. 결과적으로 이 클래스의 인스턴스를 생성하지 않는다.
@Module()
데코레이터 내에 CatsController
를 포함하는 controllers
배열을 넣어준다.
import { Module } from "@nestjs/common";
import { CatsController } from "./cats/cats.controller";
@Module({
controllers: [CatsController],
})
export class AppModule {}