Nest의 컨트롤러는 클라이언트의 요청을 처리하고 응답하는 역할을 합니다. 라우팅 메커니즘은 특정 요청을 처리할 컨트롤러를 지정합니다. 보통 각 컨트롤러는 하나 이상의 라우트를 갖고 있으며, 서로 다른 라우트는 각기 다른 역할을 수행할 수 있습니다.
기본 컨트롤러를 생성하려면 클래스와 데코레이터를 사용합니다. 데코레이터는 클래스를 메타데이터와 묶어주며 Nest를 통해 라우팅 맵(요청을 해당하는 컨트롤러와 묶는 역할)을 만드는 데 사용됩니다.
컨트롤러는 직접 소스 코드를 작성해도 되지만 다음 CLI를 통해 간편하게 생성할 수도 있습니다.
$ nest g controller [name]
이 외에도 다음과 같은 명령어를 통해 Nest 모듈, 서비스, 파이프 등을 설치할 수 있습니다.
라우팅
이어지는 예시에서 우리는 @Controller()
데코레이터를 사용할 것입니다. 클래스 위에 컨트롤러 데코레이터를 선언하면 해당 클래스는 컨트롤러 역할을 하게 됩니다.
cats
라는 라우트 패스를 지정해보겠습니다. @Controller()
내에 패스 경로를 사용하여 관련 라우트를 쉽게 그룹화할 수 있으며 코드 반복을 최소화할 수 있습니다.
예를 들어, /customers
라는 라우트 내에서 고객과 상호작용하는 일련의 라우트를 그룹화할 수 있습니다. 이 경우에는 패스 customers
를 @Controller()
데코레이터 내에 넣어주면 파일 내 라우트마다 해당 패스를 반복하지 않아도 됩니다.
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
CLI를 사용하여 컨트롤러를 생성하려면 $ nest g controller cats
커맨드를 사용할 수 있습니다.
코드를 살펴보겠습니다. findAll()
앞의 @Get()
HTTP 요청 메소드 데코레이터는 HTTP 요청에 대해 특정 앤드포인트 핸들러 생성을 명령합니다. 엔드포인트는 HTTP 요청 메소드(이 경우에는 GET) 및 라우트 패스와 일치됩니다.
여기서 라우트 패스(route path)란 무엇일까요? 핸들러를 위한 라우트 패스는 컨트롤러에 대해 선언된 일련의 문자열을 의미하며, 모든 패스는 메소드의 데코레이터에 명시됩니다.
우리는 라우트(cats
)를 선언했으며, 데코레이터에 다른 패스 정보를 입력하지 않았기 때문에 Nest는 이 핸들러에 GET /cats
요청을 매핑합니다.
앞서 언급한 것처럼 패스는 요청 메소드 데코레이터 내에 선언된 문자열을 포함합니다. 예를 들어, customers
의 패스와 @Get('profile')
이 결합되면 요청은 GET /customers/profile
과 같이 매핑됩니다.
위의 예시에서 GET 요청이 엔드포인트에 전달되었을 때, Nest는 사용자가 정의한 findALL()
메소드로 요청을 라우트합니다. 이 메소드의 경우 하나의 스트링과 함께 상태 코드 200이 전달됩니다.
Nest는 기본적으로 두 가지 응답 방식을 갖고 있습니다.
- 일반(추천)
- 기본 내장 메소드로 요청 핸들러가 자바스크립트 객체 또는 배열을 반환하면 자동으로 JSON으로 시리얼라이즈됩니다.
- 자바스크립트 원시 타입(
string
,number
,boolean
)을 리턴하는 경우에는 시리얼라징을 하지 않고 값을 전송합니다. - 상태 코드의 기본 값은 언제나 200이며, POST의 경우에는 201입니다. 상태 코드는
@HttpCode(...)
데코레이터를 사용하여 변경할 수 있습니다.
- 라이브러리 특화
- Express와 같은 라이브러리에 특화된 응답 객체를 사용할 수도 있습니다.
@Res()
데코레이터를 매소드 핸들러 서명에findAll(@Res() response)
과 같이 사용할 수 있습니다.
요청 객체
핸들러는 보통 클라이언트 요청의 세부 정보에 접근해야 할 수 있습니다. Nest는 기본 플랫폼(Express)의 요청 객체에 접근할 수 있습니다. 핸들러 서명에 @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';
}
}
요청 객체는 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 |
리소스
앞서 우리는 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는 데코레이터를 통해 모든 HTTP 메소드 @Get()
, @Post()
, @Put()
, @Delete()
, @Patch()
, @Options()
, @Head()
를 제공합니다. @All()
을 사용하면 이 모든 것을 처리하는 엔드포인트를 정의할 수 있습니다.
라우트 와일드카드
패턴 기반의 라우트도 설정할 수 있습니다. 별표(*
)를 사용하여 일치하는 문자의 모든 조합을 처리할 수 있습니다.
@Get('ab*cd')
findAll() {
return 'This route uses a wildcard'
}
ab*cd
라우트 패스는 abcd
, ab_cd
, abecd
등과 매치됩니다. 라우트 패스 안에 ?
, +
, *
등을 사용할 수도 있으며 이는 정규 표현식의 일부입니다. -
와 .
은 기본적으로 스트링 기반 패스로 인식됩니다.
상태 코드
앞서 언급한 것처럼 상태 코드는 기본적으로 200이며, POST의 경우에는 201입니다. @HttpCode(...)
데코레이터를 핸들러 레벨에 추가하여 상태 코드를 변경할 수 있습니다.
@Post()
@HttpCode(204)
create() {
return 'This action adds a new cat';
}
헤더
커스텀 응답 헤더를 설정하려면 @Header()
데코레이터를 사용할 수 있습니다.
@Post()
@Header('Cache-Control', 'none')
create() {
return 'This action adds a new cat';
}
리다이렉션
특정 URL로 리다이렉션을 하려는 경우 @Redirect()
데코레이터를 사용할 수 있습니다.
@Get()
@Redirect('https://nestjs.com', 301)
반환되는 값이 있는 경우 @Redirect()
데코레이터를 통과하는 아규먼트가 오버라이드됩니다.
@Get('docs')
@Redirect('https://docs.nestjs.com', 302)
getDocs(@Query('version') version) {
if (version && version === '5') {
return { url: 'https://docs.nestjs.com/v5/'}
}
}
라우트 파라미터
라우트 파라미터는 패스 파라미터라고도 합니다. 요청으로 동적 데이터를 받는 경우 고정 라우트로는 이를 처리할 수 없을 것입니다. 예를 들어, GET /cats/1
은 id가 1
인 고양이만 받을 수 있습니다. 파라미터로 라우트를 정의하려면 라우트 패스에 토큰을 추가하여 요청 URL에서 동적 값을 처리할 수 있습니다.
아래 예시에서 @Get()
데코레이터 내 라우트 파라미터 토큰을 확인해보시기 바랍니다. 라우트 파라미터는 @Param()
데코레이터를 사용하여 접근할 수 있으며 메소드 서명에 추가되어야 합니다.
@Get(':id')
findOne(@Param() params): string {
console.log(params.id);
return `This action returns a #${params.id} cat`;
}
파라미터가 여러 개라면 다음과 같이 구성할 수 있습니다.
@Delete(':userId/memo/:memoId')
DeleteUserMemo(
@Param('userId') userId: string,
@Param('memoId') memoId: string,
) {
return `userId: ${userId}, memoId: ${memoId}`;
}
쿼리 파라미터 받기
라우트의 쿼리를 받아오고 싶다면 다음과 같이 구성할 수 있습니다.
@Get('oauth/callback/kakao')
kakaoLoginCode(@Query('code') code: string): string {
return `${code}`;
}
서브 도메인 라우팅
@Controller
데코레이터는 host
옵션을 사용하여 특정 값과 일치하는 요청의 HTTP 호스트를 처리할 수 있습니다.
기존의 메인 도메인 내에 새로운 하위 도메인이 생겼다고 해보겠습니다. 다음과 같이 이를 명시해주면 하위 도메인으로 들어오는 요청을 처리할 수 있습니다.
@Controller({ host: 'admin.example.com'})
export class AdminController {
@Get()
index(): string {
return 'Admin page';
}
}
페이로드 요청
페이로드란 POST, PUT, PATCH와 같은 요청에서 함께 전달되는 데이터 덩어리(body)를 의미합니다.
이전 예시에서 POST 라우트 핸들러는 클라이언트로부터 어떠한 파라미터도 받지 않았습니다. 이번에는 @Body()
데코레이터를 추가하여 이를 수정해보겠습니다.
타입스크립트를 사용하고 있다면 DTO(Data Transfer Object) 스키마를 설정해야 합니다. DTO는 데이터가 네트워크에 전송되는 방법을 규정하는 객체입니다. 이는 타입스크립트 인터페이스나 클래스를 사용하여 설정할 수 있습니다.
DTO 생성 시에는 클래스를 이용하는 게 좋은데, 이는 클래스가 자바스크립트 ES6 표준이며 컴파일된 자바스크립트에서 보존되기 때문입니다. 반면, 타입스크립트 인터페이스는 트랜스파일레이션이 진행되면 사라져 Nest 런타임에서 이를 참조할 수 없습니다.
CreateCatDto
클래스를 생성해보겠습니다.
export class CreateCatDto {
name: string;
age: number;
hobby: string;
}
이제 이를 페이로드를 처리하는 CatsController
내에서 사용할 수 있습니다.
@Post()
create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}
전체 예시
다음은 컨트롤러 CRUD를 위한 전체 기본 소스 코드입니다.
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`;
}
}
컨트롤러 사용하기
컨트롤러 설정을 마쳤다면 이를 모듈에 추가해줘야 합니다. @Module()
데코레이터가 있는 app.module.ts로 이동하여 앞서 만든 CatsController
를 추가해줘야 Nest에서 해당 컨트롤러를 사용할 수 있습니다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class AppModule {}
참고 자료
더 읽어볼 자료
- 유저 서비스 인터페이스 구성하기: https://wikidocs.net/158494
'개발 > NestJS' 카테고리의 다른 글
NestJS 기초 (4) 데이터베이스 연동 (0) | 2022.10.01 |
---|---|
NestJS 기초 (2) 프로젝트 시작하기 (0) | 2022.09.29 |
NestJS 기초 (1) NestJS란? (0) | 2022.09.29 |