Skip to content

2. Controllers


컨트롤러는 들어오는 요청을 처리하고 클라이언트에 응답을 반환하는 역할을 한다.


001


컨트롤러의 목적은 애플리케이션에 대한 특정 요청을 수신하는 것이다. 라우팅 매커니즘은 어떤 컨트롤러가 어떤 요청을 수신하는지 제어한다. 종종 각 컨트롤러에는 둘 이상의 라우트가 있고 다른 라우트는 다른 작업을 수행할 수 있다.


기본 컨트롤러를 만들기 위해 클래스와 데코레이터를 사용한다. 데코레이터는 클래스를 필수 메타데이터와 연결하고 Nest가 라우팅 맵을 생성할 수 있도록 한다.


1. Routing

다음 예제에서는 기본 컨트롤러를 정의하는 데 필요한 @Controller() 데코레이터를 사용한다. cats의 라우트 경로(path) 접두사를 지정한다. @Controller() 데코레이터에서 경로 접두사를 사용하면 관련 라우트 집합을 쉽게 그룹화하고 반복적인 코드를 최소화할 수 있다. 예를 들어 라우트 /customers 아래에서 고객 엔터티와의 상호 작용을 관리하는 라우트 집합을 그룹화하도록 선택할 수 있다. 이 경우 파일의 각 경로에 대해 경로의 해당 부분을 반복할 필요가 없도록 @Controller() 데코레이터에서 경로 접두사 customers를 지정할 수 있다.


cats.controller.ts
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에 지시하여 요청 객체에 액세스할 수 있다.


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";
  }
}


요청 객체는 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 핸들러를 생성한다.


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는 @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), @Head()와 같은 모든 표준 HTTP 메서드에 대한 데코레이터를 제공한다. 또한 @All()은 모든 것을 처리하는 엔드포인트를 정의한다.


4. Route wildcards

패턴 기반 라우트도 지원된다. 예를 들어 애스터리스크는 와일드카드로 사용되며 모든 문자 조합과 일치한다.


@Get('ab*cd')
findAll() {
  return 'This route uses a wildcard';
}


'ab*cd' 라우트 경로는 abcd, ab_cd, abecd 등과 일치한다. ?, +, *, () 문자는 라우트 경로에 사용될 수 있다.


5. Status code

언급했듯이 응답 상태 코드는 201인 POST 요청을 제외하고 기본적으로 항상 200이다. 핸들러 레벨에서 @HttpCode(…) 데코레이터를 추가하여 쉽게 변경할 수 있다.


@Post()
@HttpCode(204)
create() {
  return 'This action adds a new cat';
}


Note

@nestjs/common 패키지에서 HttpCode를 임포트한다.


6. Headers

사용자 정의 응답 헤더를 지정하려면 @Header() 데코레이터 또는 응답 객체를 사용하여 res.header()를 직접 호출할 수 있다.


@Post()
@Header('Cache-Control', 'none')
create() {
  return 'This action adds a new cat';
}


Note

@nestjs/common 패키지에서 Header를 임포트한다.


7. Redirection

응답을 특정 URL로 리다이렉션하려면 Redirect() 데코레이터 또는 응답 객체를 사용하여 res.redirect()를 직접 호출할 수 있다. @Redirect()urlstatusCode라는 두 개의 인수를 사용하며 둘 다 옵셔널이다. 생략하면 statusCode의 기본값은 302(Found)이다.


@Get()
@Redirect('https://nestjs.com', 301)


때로는 HTTP 상태 코드 또는 리다이렉션 URL을 동적으로 결정해야 하는 경우도 있다. 다음과 같은 형태를 가진 라우트 핸들러에서 객체를 반환하여 이 작업을 수행한다.


{
  "url": string,
  "statusCode": number
}


반환된 값은 @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

정적 경로가 있는 라우트는 요청의 일부로 동적 데이터를 수락해야 하는 경우 작동하지 않는다(예: id1인 고양이를 가져오기 위해 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 매개변수에 액세스할 수 있다. 특정 매개변수 토큰을 데코레이터에 전달한 다음 메서드 바디에서 이름으로 직접 라우트 매개변수를 참조할 수도 있다.


@Get(':id')
findOne(@Param('id') id: string): string {
  return `This action returns a #${id} cat`;
}


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)할 수 있는 값을 반환할 수 있다.


cats.controller.ts
@Get()
async findAll(): Promise<any[]> {
  return [];
}


또한 Nest 라우트 핸들러는 RxJS 옵저버블 스트림을 반환할 수 있다.


cats.controller.ts
@Get()
findAll(): Observable<any[]> {
  return of([]);
}


11. Request payloads

POST 라우트 핸들러의 이전 예제에서는 클라이언트 매개변수를 허용하지 않았다. 여기에 @Body() 데코레이터를 추가하여 이 문제를 해결해 보자.


그러나 먼저 DTO(Data Transfer Object) 스키마를 결정해야 한다. DTO는 데이터가 네트워크를 통해 전송되는 방법을 정의하는 객체이다. TypeScript 인터페이스를 사용하거나 간단한 클래스를 사용하여 DTO 스키마를 결정할 수 있다.


CreateCatDto 클래스를 생성해 보자.


create-cate.dto.ts
export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}


CatsController 내에서 새로 생성된 DTO를 사용할 수 있다.


cats.controller.ts
@Post()
async create(@Body() createCatDto: CreateCatDto) {
  return 'This action adds a new cat';
}


12. Full resource sample

다음은 사용 가능한 여러 데코레이터를 사용하여 기번 컨트롤러를 만드는 예제이다. 이 컨트롤러는 내부 데이터에 액세스하고 조작하는 몇 가지 방법을 제공한다.


cats.controller.ts
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 배열을 넣어준다.


app.module.ts
import { Module } from "@nestjs/common";
import { CatsController } from "./cats/cats.controller";

@Module({
  controllers: [CatsController],
})
export class AppModule {}

References