Nest Controllers

정민교·2023년 8월 18일
0

NestJS

목록 보기
2/4

📒Controllers

컨트롤러의 책임은 클라이언트의 요청을 처리하고 응답을 반환하는 것입니다.

라우팅 메커니즘은 앱에 대한 요청을 어떤 컨트롤러가 처리해야하는지 결정합니다.

보통 하나의 컨트롤러는 하나 이상의 라우트를 가지고 있으며 각 라우트들은 서로 다른 액션을 취할 수 있습니다.

컨트롤러를 만들기 위해서는 클래스와 데코레이터를 사용합니다. 데코레이터는 클래스와 필요한 메타 데이터들을 연결하고, 요청을 컨트롤러와 연결하는 routing map을 생성할 수 있도록 합니다.

그러면 users controller를 만들어 봅시다.

✔️Routing

import { Controller, Get } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get()
  findAll(): string {
    return 'This actions returns all cats';
  }
}

위와 같이 클래스 위에 @Controller() 데코레이터를 호출하면 해당 클래스는 데코레이터의 인수로 넘겨진 문자열을 api 호출 base 경로로 처리하는 컨트롤러가 됩니다.

그리고 클래스 내의 메서드에는 HTTP 메서드에 해당하는 데코레이터를 호출하여 해당 엔드포인트 요청에 대한 핸들러로 만들어 줄 수 있습니다.

메서드에 붙은 HTTP 메서드에 해당하는 데코레이터에도 url 경로를 인수로 넘겨줄 수 있으며, 이렇게 해서 Controller에 지정한 base 라우트 아래의 User 엔티티와 관련된 라우트들을 그룹화 할 수 있습니다.

현재는 @Get() 데코레이터에 아무것도 넘겨주지 않았습니다. 따라서 findAll() 메서드는 GET /users 를 처리하는 api가 됩니다.

import { Controller, Get, HttpCode } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get()
  @HttpCode(200)
  findAll(): string {
    return 'This actions returns all cats';
  }
}

@HttpCode 데코레이터를 통해 상태코드 추가도 가능합니다.

Nest는 기본적으로 객체나 배열을 반환하는 경우 자동으로 JSON으로 직렬화 해주지만, number, string, boolean 등의 원시 타입 값을 반환하는 경우에는 JSON으로 직렬화하지 않습니다.

아니면 @Res() 데코레이터를 사용해서 특정 응답 객체를 주입받을 수 있습니다.

import { Controller, Get, HttpCode, Res } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get()
  @HttpCode(200)
  findAll(@Res() response): string {
    return response.status(200).send('This actions returns all users');
  }
}

이렇게 하면 응답 객체에 노출되어있는(exposed) 기본적인 응답 처리 메서드를 사용할 수 있습니다.

위는 Express를 사용한 예입니다.

✔️Request object

핸들러에서 보통 요청에 대한 상세 정보가 필요한 경우가 많습니다.

Nest에서는 기본 플랫폼(Express가 기본값)의 요청 객체에 대한 접근할 수 있습니다.

핸들러 시그니처에 @Req() 데코레이터를 추가해서 Nest가 요청 객체(request object)를 주입하도록 만들 수 있습니다.

import { Controller, Get, HttpCode, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller('users')
export class UsersController {
  @Get()
  @HttpCode(200)
  findAll(@Req() request: Request): string {
    return 'This actions returns all users';
  }
}

HTTP 요청을 나타내는 request 객체는 쿼리 스트링, 파라미터, HTTP 헤더, 바디 등의 속성을 가지고 있습니다.

Express에서 주로 사용하던 것들입니다. 주로 req.query 이런식으로 사용해왔는데 Nest에서는 이러한 속성을 바로 가져오는 데코레이터들을 사용할 수 있습니다.

@types/express를 설치해서 타이핑의 이점을 누릴 수 있도록 하는 것이 좋습니다.

✔️핸들러가 처리하는 HTTP 메서드

Nest에서는 간단하게 모든 기본 HTTP method 종류를 데코레이터로 제공합니다.

@Post(), @Put(), @Delete(), @Patch(), @Options(), and @Head().

@All() 은 모든 종류의 HTTP 메서드를 처리하는 핸들러로 정의할 수 있습니다.

✔️Route wildcards

route에 Express에서 사용하는 문자열 패턴 매칭과 와일드 카드도 사용 가능합니다.

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

✔️Headers

커스텀 응답 헤더를 명시하기 위한 @Header()를 사용할 수 있습니다.

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

✔️Redirection

리다이렉트를 위해서 Nest에서는 @Redirect() 데코레이터를 제공합니다.

@Redirect() 데코레이터는 두 url: string, statusCode: number를 매개변수로 갖습니다.

이 둘은 선택값이고 생략되면 302가 기본입니다.

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

리다이렉트 url과 statuc code를 동적으로 결정하고 싶다면 아래와 같은 형태의 객체를 핸들러 메서드의 응답으로 사용하면 됩니다.

{
  "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/' };
  }
}

✔️Route Parameters

요청 URL에서 특정 위치의 동적 값을 캡처하기 위해 라우트 경로에 라우트 매개변수 토큰을 추가할 수 있습니다.

선언된 라우트 파라미터는 핸들러 메서드의 시그니처에 @Param() 데코레이터를 추가해서 접근할 수 있습니다.

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

@Param() 데코레이터를 메서드 파라미터에 추가하면, 위와 같이 라우트 파라미터들을 데코레이팅된 메서드 파라미터의 속성으로 사용할 수 있습니다.

아니면 아래와 같이 특정 라우트 파라미터 토큰을 데코레이터의 인수로 전달하여 라우트 파라미터 이름으로 값에 직접 접근할 수 있습니다.

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

✔️Sub-Domain Routing

@Controller 데코레이터는 호스트 옵션을 사용하여 들어오는 요청의 HTTP 호스트가 명시된 값과 일치하는지 확인할 수 있습니다.

@Controller({ host: 'admin.example.com' })
export class AdminController {
  @Get()
  index(): string {
    return 'Admin page';
  }
}

host 옵션으로 host name의 특정 위치에 동적 값을 캡쳐할 수 있습니다.

@Controller({ host: ':account.example.com' })
export class AccountController {
  @Get()
  getInfo(@HostParam('account') account: string) {
    return account;
  }
}

@Controller 데코레이터의 host 옵션에 지정된 host parameter 토큰입니다. 이렇게 선언된 host parameter는 메서드 시그니처에 @HostParam() 데코레이터를 추가함으로써 접근할 수 있습니다.

✔️Scope

Node.js는 request/response Multi-Threaded Stateless Model(모든 요청이 각각 별도의 스레드에 의해 처리되는 모델)을 따르지 않습니다. 따라서 Node에서 싱글톤 인스턴스를 쓰는 것은 안전합니다.

✔️Asynchronicity

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

async 함수는 반드시 Promise를 반환합니다. Nest 라우트 핸들러에서 값을 반환하면 스스로 resolve 된 값을 반환할 수 있습니다.

또한 라우트 핸들러는 RxJS 옵저버블 스트림을 반환할 수 있습니다. Nest는 자동으로 아래의 소스를 구독하고 스트림이 완료되면 마지막으로 방출된 값을 가져옵니다.

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

✔️Reuqest Payloads

@Body() 데코레이터를 추가하여 클라이언트의 요청 페이로드에 접근할 수 있습니다.

TypeScript를 사용하고 있다면 DTO 스키마를 먼저 작성해야 합니다.

TS 인터페이스나 클래스를 사용해서 DTO 스키마를 작성할 수 있습니다. 클래스를 사용하는 것을 추천하는데 클래스는 ES6 표준이기 때문에 TS를 컴파일한 JS에도 그대로 클래스가 보존되기 때문입니다.

인터페이스를 사용하면 컴파일된 JS 파일에서는 사라지기 때문에 runtime에 Nest가 참조할 수 없게 됩니다.

Pipe같은 기능으로 runtime 때 변수의 메타타입에 접근할 가능성이 있기 때문에 클래스로 DTO를 정의하는 것이 좋습니다.

export class CreateCatDto {
  name: string;
  age: number;
  breed: string;
}

이렇게 DTO 클래스를 정의하고

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

DTO를 라우트 핸들러 시그니처에 매개변수의 타입으로 지정하여 요청 페이로드를 DTO 타입으로 전달받을 수 있습니다.

profile
백엔드 개발자

0개의 댓글