컨트롤러(Controller)의 목적은 특정 request를 처리하고 클라이언트에 response를 반환하는 역할을 하는 핵심합니다. 컨트롤러(Controller)는 클라이언트로부터 들어온 GET, POST, PUT, DELETE 등의 HTTP requset를 처리하며, 각 요청을 적절한 핸들러 메서드에 매핑하여 response를 반환합니다. 이러한 과정은 라우팅 메커니즘을 통해 이루어집니다.
컨트롤러(Controller)를 생성하기 위해서는 데코레이터(decorator)를 사용합니다. 데코레이터(decorator)는 클래스에 필요한 메타데이터와 연결하고 Nest가 라우팅 맵을 생성할 수 있도록 합니다.
HINT
Nest에서 사용되는 데코레이터(decorator)는 메타프로그래밍을 지원하는 TypeScript 기능을 기반으로 하며, 클래스, 메서드, 프로퍼티, 매개변수 등에 추가적인 기능**을 정의할 수 있도록 돕는 기능입니다.
→ [Controller 정의]
컨트롤러(Controller)를 정의하기 위해서는 @Controller
데코레이터를 사용합니다. @Controller
는 특정 prefix(경로 접두사)를 설정하여 해당 경로와 관련된 모든 라우터를 처리할 수 있습니다.
예를 들어, 고양이 엔티티와 상호작용하는 모든 라우트를 /cats
경로 아래에 그룹화할 수 있습니다.
// cats.controller.ts
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
여기서 @Get()
데코레이터는 HTTP GET 요청을 처리하는 핸들러 메서드인 findAll()
을 정의합니다.
→ [라우트 경로]
@Get
, @Post
, @Put
, @Delete
, etc…)에 지정된 경로를 결합하여 라우트가 결정됩니다.@Controller(’cats’)
와 @Get(’bread’)
를 결합하면 GET/cates/bread
라우트가 생성됩니다.→ [표준 응답 처리(recommended)]
string
, number
, boolean
)을 반환할 경우에는 Nest는 직렬화를 시도하지 않고 기본 값을 반환합니다.@HttpCode(...)
데코레이터를 통해 상태 코드를 변경할 수 있습니다.→ [라이브러리별 응답 처리]
response object
를 직접 사용할 수 있습니다. 이를 위해 @Res()
데코레이터를 사요합니다.@Get()
findAll(@Res() resposne) {
return response.status(200).send('This action returns all cats')
}
@Res()
데코레이터를 사용하면 표준 응답 처리 방식이 비활성됩니다. 만약 response object
를 사용하면서도 나머지 처리를 Nest에 맡기고 싶다면, @Res({ passthrough: true })
옵션을 사용할 수 있습니다.HINT
Nest CLI 명령어를 사용해서 쉽게 컨트롤러를 생성할 수 있습니다.
$ nest g controller [name]
명령어를 실행하기만 하면 됩니다.
Nest는 기본적으로 Express나 Fastify 등의 플랫폼에서 requset object(요청 객체)를 처리하며, @Req()
데코레이터를 사용해서 핸들러에서 직접 요청 객체에 접근할 수 있습니다.
// cats.controller.ts
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';
}
}
HINT
위 예제처럼request: Request
타입을 추가하기 위해서 express 타입을 활용할 수 있는@types/express
패키지를 설치하세요.
@Req()
데코레이터를 사용한 요청 객체(request object)는 HTTP 요청을 나타내며 requset query string
, parameters
, HTTP headers
및 body
에 대한 프로퍼티를 가지고 있습니다. 대부분의 경우 이런 속성을 수동으로 가져올 필요 없이 @Body()
, @Query()
, @Param()
등의 데코레이터를 통해 즉시 사용할 수 있습니다. 아래는 Nest에서 제공하는 요청 객체(Request object) 데코레이터입니다.
@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 |
응답 객체(response object)를 사용할 때는 @Res()
또는 @Response
데코레이터를 통해 기본 HTTP 플랫폼(Express 및 Fastify) 응답 객체를 직접 관리할 수 있습니다. @Res()
또는 @Response
를 사용하면 Nest 라이브러리 전용 모드(Library-specific)로 전환하고 응답(Response)을 관리할 책임이 사용자에게 있다는 점을 유의하세요. 라이브러리 전용 모드를 사용하게 된다면 response
객체(예: res.json(...)
또는 res.send(...)
)를 호출하여 응답을 보내야 하며, 그렇지 않으면 HTTP 서버가 중단됩니다.
Nest에서는 HTTP 메서드 데코레이터(@Get()
, @Post()
, @Put()
, @Delete()
, @Patch()
, @Options()
, @Head()
)를 통해 리소스 엔트포인트를 정의할 수 있습니다. , 또한 @All
데코레이터는 모든 메서드를 처리하는 에든포인트를 정의할 수 있습니다.
import { Controller, Get, Post } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
@Post()
create(): string {
return 'This action adds a new cat';
}
@Put()
modify(): string {
return 'This action modify a cat';
}
@Delete()
delete(): string {
return 'This action delete a cat';
}
}
엔드포인트에 와이드카드 경로를 사용하면 어떠한 문자 조합과도 메서드를 일치시킬 수 있습니다.
@Get('ab*cd')
findAll() {
return 'This route uses a wildcard';
}
ab*dc
라우트 경로는 abcd
, ab_cd
, adecd
등과 일치합니다. 문자 ?
, +
, *
, ()
는 라우트 경로에 사용할 수 있으며 정규표현식입니다. 하이픈(-
),과 점(.
)은 문자 그대로 해석됩니다.
HINT
Express에서만 중간 와이드카드가 지원된다는 점을 유의하세요.
응답 상태 코드는 기본적으로 201인 POST 요청을 제외하고 항상 200입니다. 핸들러 수준에서 @HttpCode(...)
데코레이터를 추가하면 쉽게 응답 상태 코드를 변경할 수 있습니다.
@Post()
@HttpCode(204)
create() {
return 'This action adds a new cat';
}
HINT
@nestjs/common
패키지에서HttpCode
를 가져옵니다.
상태 코드는 정적이지 않은 경우 @Res
또는 @Response
데코레이터를 사용해서 라이브러리별로 응답 객체를 사용할 수 있으며, 오류가 발생하면 예외를 발생시킬 수 있습니다.
@Header
데코레이터 또는 @Res
(라이브러리별 응답 객체 res.header()
)를 사용해서 커스텀 응답 헤더를 지정할 수 있습니다.
HINT
@nestjs/common
패키지에서Header
를 가져옵니다.
응답을 특정 URL로 리다이렉션하려면 @Redirect(url?: string, statusCode?: number)
또는 @Res() → res.redirect()
를 직접 호출해야 합니다.
@Get('default')
@Redirect('https://nestjs.com', 301)
@Get('use-res')
redirectToNewRoute(@Res() res: Response) {
return res.redirect('/new-route');
}
@Redirect()
데코레이터는 두개의 인수, url
과 statusCode
를 선택적으로 받을 수 있습니다. statusCode
의 기본값은 생략할 수 경우 302(Found)
입니다.
반환된 값은 @Redirect()
데코레이터에 전달된 모든 인수를 재정의합니다. 예를 들어:
@Get('docs')
@Redirect('https://docs.nestjs.com', 302)
getDocs(@Query('version') version) {
if (version && version === '5') {
return { url: 'https://docs.nestjs.com/v5/' };
}
}
Nest에서 Route Parameters(라우트 매개변수)는 URL 경로에서 변수를 받아와 동적으로 처리하는 기능입니다. @Param()
데코레이터를 사용하여 요청 경로의 변수를 캡쳐할 수 있습니다. 예를 들어 id
와 같은 매개변수는 URL의 일부로 전달되어 핸들러 함수에서 처리할 수 있습니다.
@Get(':id')
getUserById(@Param('id') id: string) {
return `User ID is: ${id}`;
}
이 코드는 /cats/123
과 같은 요청을 처리하면, id
값이 함수로 전달됩니다.
→ [라우트 매개변수의 주요 개념]
@Get(':userId/:postId')
getPost(@Param('userId') userId: string, @Param('postId') postId: string) {
return `User ID: ${userId}, Post ID: ${postId}`;
}
위 코드는 GET cats/123/12
와 같은 URL에서 두 개의 매개변수를 동적으로 받아서 처리합니다.
→ [라우트 매개변수와 쿼리 매개변수의 차이]
@Params
데코레이터 사용)?key=value
형식으로 전달됩니다. 검색 필터와 같은 정보를 전달할 때 사용할 수 있습니다.(@Query
데코레이터를 사용합니다.)// route parameter example
// client request: GET /usrs/123
@Get('users/:id')
getUserById(@Param('id') id: string) {
return `User ID: ${id}`;
}
// query parameter example
// client requset: GET /users?page-2
@Get('users')
getUserByQuery(@Query('page') page: number) {
return `Page: ${page}`;
}
→ [동적 라우트 매개변수 사용 시 주의 사항]
동적 라우트 경로를 정적 라우트 경로 뒤에 선언해야 하는 규칙이 있습니다. 이유는 매개변수가 있는 경로는 여러 패턴과 일치할 수 있기 때문에, 정적 경로가 먼저 선언되지 않으면 동적 경로가 먼저 처리되어 정적 경로에 해당하는 요청도 동적 경로로 전달될 수 있기 때문입니다.
@Get('profile')
getProfile() {
return 'Profile Page';
}
@Get(':username')
getUserByUsername(@Param('username') username: string) {
return `User: ${username}`;
}
위 예시에서, /profile
과 /john
같은 경로가 있다고 가정하면, 정적 경로(/profile
)를 먼저 선언하고 다음에 동적 경로를 선언해야 매개변수화된 경로가 정적 경로의 요청을 가로채지 않습니다.
서브 도메인 라우팅은 특정 서브 도메인을 처리해야 할 경우에 사용됩니다. 예를 들어 서브 도메인을 운영하는 서비스에서 각각의 서브 도메인에 대한 다른 로직을 처리해야 할 때 유용합니다.
Nest에서 서브 도메인 라우팅을 사용하기 위해서는 @Controller
데코레이터에 host
옵션 추가하여 특정 HTTP host와 일치시키도록 할 수 있습니다.
@Controller({ host: 'admin.example.com' })
export class AdminController {
@Get()
index(): string {
return 'Admin page';
}
}
서브 도메인 라우팅에서 동적 값을 처리하는 방법
@Controller()
에서 host parameter token을 사용하여 서브 도메인의 동적 값을 캡쳐할 수 있습니다.@HostParam()
데코레이터를 통해 메서드에 접근할 수 있습니다.HINT
host parameter token은 서브 도메인에서 동적 값을 캡쳐하기 위한 방식입니다. 예를 들어, 도메인이{username}.example.com
일 경우,{username}
이 host parameter token으로 동작하여 사요자 이름을 동적으로 캡쳐할 수 있습니다.
@Controller({ host: ':username.example.com' })
export class UserController {
@Get()
getUser(@HostParam('username') username: string) {
return `Hello, ${username}`;
}
}
Nest에서 Request Payload는 클라이언트가 서버로 보내는 데이터를 의미하며, 주로 POST
, PUT
, PATCH
요청에서 사용됩니다. 데이터를 처리하기 위해 @Body()
데코레이터를 사용해서 Request Payload를 처리할 수 있습니다. 타입스크립트로 Nest 프로젝트를 구축하는 경우. DTO(Data Transfer Object)를 사용해서 입력 데이터(request payload)의 구조를 명확하게 정의하고, 데이터의 일관성을 유지하고, 유효성 검사를 쉽게 적용할 수 있습니다.
export class CreateDto {
@IsString()
name: string;
@IsNumber()
age: number;
}
@Post()
create(@Body() createDto: CreateDto) {
return this.service.create(createDto);
}
→ [DTO를 클래스로 정의하는 이유]