Nest Js 컨트롤러

Node/Nest Js 2020. 1. 28. 15:06

컨트롤러

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

  • 컨트롤러의 목적은 응용 프로그램에 대한 특정 요청을 받는 것입니다.

  • 라우팅 메커니즘은 어떤 컨트롤러가 어떤 요청을 받는지 제어합니다.

  • 종종 각 컨트롤러에는 둘 이상의 경로가 있으며 다른 경로는 다른 작업을 수행 할 수 있습니다.

 

라우팅

  • 다음 예제에서는 기본 컨트롤러를 정의하는 데 필요한 @Controller() 데코레이터를 사용합니다.

  • cats를 선택적 라우트 경로 접두사를 지정합니다.

  • @Controller() 데코레이터에서 경로 접두사를 사용하면 관련 경로 세트를 쉽게 그룹화하고 반복 코드를 최소화 할 수 있습니다.

  • 예를 들어

    • /cumstomers 라우트 아래 고객 엔티티와의 상호 작용을 관리하는 경로 세트를 그룹화하도록 선택할 수 있습니다.

    • 이 경우 @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';
  }
}

 

HINT

CLI를 사용하여 컨트롤러를 작성하려면 $ nest g controller cats 명령을 실행하십시오.

주의, CLI를 사용하여 작성한 컨트롤러는 @Get() 데코레이터를 임포트 하지 않을 수도 있습니다.

 

  • findAll() 메소드 앞의 @Get() HTTP 요청 메소드 데코레이터는 Nest에게 HTTP 요청에 대한 특정 엔드 포인트에 대한 핸들러를 작성하도록 지시합니다.

  • 엔드 포인트는 HTTP 요청 메소드 (이 경우 GET) 및 라우트 경로에 해당합니다.

  • 라우트 경로는 무엇입니까?

    • 핸들러의 라우트 경로는 제어기에 선언 된 (선택적) 접두사와 요청 데코레이터에 지정된 경로를 연결하여 결정됩니다.

  • 모든 경로(cats)에 대한 접두사를 선언하고 데코레이터에 경로 정보를 추가하지 않았기 때문에 Nest는 GET /cats 요청을이 핸들러에 매핑합니다.

  • 언급 한 바와 같이, 경로에는 선택적 컨트롤러 경로 접두사 요청 메소드 데코레이터에 선언 된 경로 문자열이 모두 포함됩니다.

  • 예를 들어

    • 데코레이터 @Get ('profile')과 결합 된 cumtomers의 경로 접두사는 GET /customers/profile과 같은 요청에 대한 경로 매핑을 생성합니다.

 

  • 위의 예에서 GET 요청이 이 엔드 포인트에 이루어지면 Nest는 요청을 사용자 정의 findAll() 메소드로 라우팅합니다.

  • 여기서 선택한 메소드 이름은 완전히 임의적입니다.

  • 라우트를 바인딩 할 메소드를 선언해야하지만 Nest는 선택한 메소드 이름에 아무런 의미도 부여하지 않습니다.

 

  • 이 메소드는 200 상태 코드 및 연관된 응답 (이 경우 문자열)을 리턴합니다.

  • 왜 그런 일이 발생합니까?

    • 설명하기 위해 먼저 Nest가 응답을 조작하기 위해 두 가지 옵션을 사용한다는 개념을 소개합니다.

      • Standard(추천)

        • 이 내장 메소드를 사용하여 요청 핸들러가 JavaScript 오브젝트 또는 배열을 리턴하면 자동으로 JSON으로 직렬화됩니다.

        • 그러나 JavaScript 기본 타입 (예 : 문자열, 숫자, 부울)을 반환하면 Nest는 직렬화하려고 시도하지 않고 값만 보냅니다.

        • 이것은 응답 처리를 간단하게 만듭니다.

          • 값을 반환하면 Nest가 나머지를 처리합니다.

        • 또한 201을 사용하는 POST 요청을 제외하고 응답의 상태 코드는 기본적으로 항상 200 입니다.

        • 핸들러 수준에서 @HttpCode (...) 데코레이터를 추가하여 이 동작을 쉽게 변경할 수 있습니다 (상태 코드 참조).

      • Library-specific

        • 메소드 핸들러 서명 (예 : findAll (@Res () response))에서 @Res () 데코레이터를 사용하여 주입 할 수있는 라이브러리 별 (예 : Express) response 객체를 사용할 수 있습니다.

        • 이 접근 방식을 사용하면 해당 객체에 의해 노출 된 기본 응답 처리 방법을 사용할 수 있습니다 (및 책임).

        • 예를 들어

          • Express를 사용하면 response.status (200) .send ()와 같은 코드를 사용하여 응답을 구성 할 수 있습니다.

경고

두 가지 방법을 동시에 사용할 수는 없습니다.

Nest는 핸들러가 @Res() 또는 @Next()를 사용할 때를 감지하여 라이브러리 별 옵션을 선택했음을 나타냅니다.

두 접근 방식을 동시에 사용하면이 단일 경로에 대해 표준 접근 방법이 자동으로 비활성화되고 더 이상 예상대로 작동하지 않습니다.

 

Request 객체

  • 핸들러는 종종 클라이언트 request 세부 사항에 액세스해야합니다.

  • Nest는 기본 플랫폼의 request 객체에 대한 액세스를 제공합니다 (기본적으로 Express).

  • 핸들러의 서명에 @Req() 데코레이터를 추가하여 Nest에게 요청하도록 요청하여 request 객체에 액세스 할 수 있습니다.

//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 패키지 설치하세요.

 

  • request 객체는 HTTP 요청을 나타내며 요청 쿼리 문자열, 매개 변수, HTTP 헤더 및 본문에 대한 속성을 갖습니다 (자세한 내용은 여기 참조).

  • 대부분의 경우 이러한 속성을 수동으로 가져올 필요는 없습니다.

  • @Body() 또는 @Query()와 같은 전용 데코레이터를 대신 사용할 수 있습니다.

  • 아래는 제공된 데코레이터와 이들이 나타내는 일반 플랫폼 별 오브젝트 목록입니다.

@Request() 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

 

  • 기본 HTTP 플랫폼 (예 : Express 및 Fastify)에서 입력과의 호환성을 위해 Nest는 @Res()@Response() 데코레이터를 제공합니다.

  • @Res()는 단순히 @Response()의 별칭 입니다.

  • 둘 다 기본 네이티브 플랫폼 response 객체 인터페이스를 직접 노출합니다.

  • 그것들을 사용할 때, 기본 라이브러리 (예 : @types/express)의 타이핑을 가져 와서 최대한 활용해야합니다.

  • 메소드 핸들러에 @Res() 또는 @Response()를 삽입 할 때 Nest를 해당 핸들러의 특정 라이브러리 모드로 설정하고 응답 관리를 담당합니다.

  • 그렇게 할 때 response 객체 (예 : res.json (...) 또는 res.send (...))를 호출하여 일종의 응답을 발행해야합니다.

    • 그렇지 않으면 HTTP 서버가 정지됩니다.

HINT

자신 만의 데코레이터를 만드는 방법을 알아 보려면 이 장을 방문하십시오.

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는 나머지 표준 HTTP 요청 엔드 포인트 데코레이터를 @Put(), @Delete(), @Patch(), @Options(), @Head() 및 @All()과 같은 방식으로 제공합니다.

  • 각각은 해당 HTTP 요청 방법을 나타냅니다.

 

Route 와일드카드

  • 패턴 기반 경로도 지원됩니다.

  • 예를 들어 별표는 와일드 카드로 사용되며 모든 문자 조합과 일치합니다.

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

 

  • 'ab * cd' 경로 경로는 abcd, ab_cd, abecd 등과 일치합니다.

  • ?, +, * 및 () 문자는 경로 경로에 사용될 수 있으며 정규 표현식의 하위 집합입니다.

  • 하이픈(-)과 점(.)은 문자 그대로 문자열 기반 경로로 해석됩니다.

 

상태 코드

  • 언급 한 바와 같이 201인 POST 요청을 제외하고 응답 상태 코드는 기본적으로 항상 200 입니다.

  • 핸들러 수준에서 @HttpCode (...) 데코레이터를 추가하여이 동작을 쉽게 변경할 수 있습니다.

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

 

HINT

@nestjs/ common 패키지에서 HttpCode를 가져옵니다.

 

  • 종종, 상태 코드는 고정적이지 않지만 다양한 요인에 의존합니다.

  • 이 경우 특정 라이브러리 response (@Res()을 사용하여 주입) 객체를 사용하거나 오류가 발생한 경우 예외를 발생시킬 수 있습니다.

 

Headers

  • 사용자 정의 응답 헤더를 지정하려면 @Header() 데코레이터 또는 특정 라이브러리 response 객체를 사용하고 res.header()를 직접 호출 할 수 있습니다.

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

 

HINT

@nestjs/common 패키지에서 Header를 가져옵니다.

 

리다이렉션

  • 응답을 특정 URL로 리디렉션하려면 @Redirect() 데코레이터 또는 특정 라이브러리 response 객체를 사용하고 res.redirect()를 직접 호출하면 됩니다.

  • @Redirect()는 필수 url 인수와 선택적 statusCode 인수를 사용합니다.

  • 생략하면 statusCode의 기본값은 302 (Found) 입니다.

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

 

  • 때로는 HTTP 상태 코드 또는 리디렉션 URL을 동적으로 결정하려고 할 수 있습니다.

  • 모양과 함께 route 핸들러 메서드에서 객체를 반환하여 합니다.

{
  "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 파라미터

  • 정적 경로가 있는 Route들은 요청의 일부로 동적 데이터를 수락해야 할 때 작동하지 않습니다.

    • ( 예 : id가 1 인 cat을 얻으려면 GET /cats/1  )

  • 매개 변수로 route들을 정의하기 위해 route의 경로에 route 매개 변수 토큰들을 추가하여 요청 URL의 해당 위치에서 동적 값을 캡처 할 수 있습니다.

  • 아래 @Get() 데코레이터 예제는 route 매개 변수 토큰 사용법을 보여줍니다.

  • 이 방법으로 선언 된 route 매개 변수들은 메소드 서명에 추가해야하는 @Param() 데코레이터를 사용하여 액세스 할 수 있습니다.

    • 메소드 서명 : 그냥 매소드 인수 파라미터 앞이라 생각하자.

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

 

  • @Param()은 메소드 매개 변수 (위 예제의 params)를 장식하는 데 사용되며, route 파라미터를 메소드 본문 내에서 데코레이트 된  메소드 매개 변수의 프로퍼티들로 사용 가능하게합니다.

  • 위 코드에서 볼 수 있듯이 params.id를 참조하여 id 매개 변수에 액세스 할 수 있습니다.

  • 특정 파라미터 토큰을 데코레이터에 전달한 다음 메서드 본문(메소드의 {} 안)에서 이름으로 직접 경로 매개 변수를 참조 할 수도 있습니다.

HINT

@nestjs/common 패키지에서 Param을 가져옵니다.

 

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

 

서브-도메인 라우팅

  • @Controller 데코레이터는 들어오는 요청의 HTTP 호스트가 특정 값과 일치하도록 host 옵션을 사용할 수 있습니다.

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

 

WARNING

Fastify는 중첩 라우터에 대한 지원이 없기 때문에 서브-도메인 라우팅을 사용할 때 (기본) Express 어댑터를 대신 사용해야합니다.

 

  • 라우트 path과 마찬가지로 hosts 옵션은 토큰을 사용하여 호스트 이름의 해당 위치에서 동적 값을 캡처 할 수 있습니다.

  • 아래 @Controller() 데코레이터 예제의 host 매개 변수 토큰 사용법을 보여줍니다.

  • 이 방법으로 선언 된 host 매개 변수는 메소드 서명에 추가해야하는 @HostParam() 데코레이터를 사용하여 액세스 할 수 있습니다.

Scopes

  • 다른 프로그래밍 언어 배경을 가진 사람들의 경우 Nest에서 거의 모든 것이 들어오는 요청에서 공유된다는 것을 배우는 것은 예상치 못한 일 입니다.

  • 데이터베이스에 대한 연결 풀, 글로벌 상태의 싱글 톤 서비스 등이 있습니다.

  • Node.js는 모든 요청이 별도의 스레드에 의해 처리되는 request/response Multi-Threaded 비 상태 모델을 따르지 않습니다.

  • 따라서 싱글 톤 인스턴스를 사용하는 것이 애플리케이션에 완전히 안전합니다.

  • 그러나 예를 들면, GraphQL 애플리케이션에서 요청 당 캐싱, 요청 추적 또는 멀티 테넌시(Multi-tenancy)와 같이 컨트롤러의 요청 기반 수명이 원하는 동작이라는 엣지케이스가 있습니다.

  • 여기서 스코프를 제어하는 방법을 알아보십시오.

 

비동기성

  • 우리는 현대 JavaScript를 좋아하며 데이터 추출이 대부분 비동기 적이라는 것을 알고 있습니다.

  • 이것이 Nest가 비동기 기능을 지원하고 잘 작동하는 이유입니다.

HINT

awync / await 기능에 대한 자세한 내용은 여기를 참조하십시오.

 

  • 모든 비동기 함수는 Promise를 반환해야합니다.

  • 즉, Nest가 자체적으로 해결할 수있는 지연된 값을 리턴 할 수 있습니다.

  • 이 예를 봅시다.

//cats.controller.ts

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

 

  • 위 코드는 완전히 유효합니다.

  • 또한 Nest route 핸들러는 RxJS observable streams를 반환 할 수있어 훨씬 강력합니다.

  • Nest는 아래의 소스를 자동으로 구독하고 마지막으로 방출 된 값을 취합니다 (스트림이 완료되면).

//cats.controller.ts

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

 

 

  • 위의 두 가지 방법이 모두 작동하며 요구 사항에 맞는 것을 사용할 수 있습니다.

Request payloads

  • POST route 핸들러의 이전 예에서는 클라이언트 매개 변수를 허용하지 않았습니다.

  • @Body() 데코레이터를 여기에 추가하여 이 문제를 해결하겠습니다.

 

  • 그러나 먼저 (TypeScript를 사용하는 경우) DTO (Data Transfer Object) 스키마를 결정해야합니다.

  • DTO는 네트워크를 통해 데이터가 전송되는 방식을 정의하는 객체 입니다.

  • TypeScript 인터페이스를 사용하거나 간단한 클래스를 사용하여 DTO 스키마를 결정 할 수 있습니다.

  • 흥미롭게도 여기에서 classes를 사용하는 것을 추천합니다.

  • 왜? 클래스는 JavaScript ES6 표준의 일부이므로 컴파일 된 JavaScript에서 실제 엔티티로 유지됩니다.

  • 반면, 변환 과정에서 TypeScript 인터페이스가 제거되므로 Nest는 런타임시 인터페이스를 참조 할 수 없습니다.

  • 이는 Pipes와 같은 기능이 런타임에 변수의 메타타입에 접근할 때 추가적인 가능성을 가능하게 하기 때문에 중요하다.

 

  • CreateCatDto 클래스를 만들어 봅시다.

//create-cat.dto.ts

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

 

  • 세 가지 기본 프로퍼티만 있습니다.

  • 그런 다음 CatsController 내부에서 새로 생성 된 DTO를 사용할 수 있습니다.

//cats.controller.ts

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

 

Handling errors

여기에는 오류 처리 (예 : 예외 작업)에 대한 별도의 장이 있습니다.

 

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

 

시작 및 실행

  • 위의 컨트롤러가 완전히 정의 된 경우 Nest는 여전히 CatsController가 존재하는지 알지 못하므로 결과적으로이 클래스의 인스턴스를 만들지 않습니다.

 

  • 컨트롤러는 항상 모듈에 속하므로 @Module() 데코레이터 내 controllers 배열에 포함시킵니다.

  • 루트 AppModule을 제외한 다른 모듈은 아직 정의하지 않았으므로 이를 사용하여 CatsController를 소개합니다.

//app.module.ts

import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';

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

 

 

  • @Module() 데코레이터를 사용하여 메타데이터를 모듈 클래스에 연결했으며 Nest는 이제 어떤 컨트롤러를 마운트해야하는지 쉽게 반영 할 수 있습니다.

부록 : 특정 라이브러리 접근

So far we've discussed the Nest standard way of manipulating responses. The second way of manipulating the response is to use a library-specific response object. In order to inject a particular response object, we need to use the @Res() decorator. To show the differences, let's rewrite the CatsController to the following:

 

import { Controller, Get, Post, Res, HttpStatus } from '@nestjs/common';
import { Response } from 'express';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Res() res: Response) {
    res.status(HttpStatus.CREATED).send();
  }

  @Get()
  findAll(@Res() res: Response) {
     res.status(HttpStatus.OK).json([]);
  }
}

 

Though this approach works, and does in fact allow for more flexibility in some ways by providing full control of the response object (headers manipulation, library-specific features, and so on), it should be used with care. In general, the approach is much less clear and does have some disadvantages. The main disadvantages are that you lose compatibility with Nest features that depend on Nest standard response handling, such as Interceptors and the @HttpCode() decorator. Also, your code can become platform-dependent (as underlying libraries may have different APIs on the response object), and harder to test (you'll have to mock the response object, etc.).

 

As a result, the Nest standard approach should always be preferred when possible.


문서를 처음부터 끝까지 다 읽으면 해당 영역에서 무슨 일이 일어나는 지 알수 있다. => 개꿀

'Node > Nest Js' 카테고리의 다른 글

Nest.js, React.js, Typescript 및 MongoDB로 최신 풀 스택 개발 - 1부  (0) 2020.02.01
Nest js 모델-뷰-컨트롤러  (0) 2020.01.31
Nest js Providers  (0) 2020.01.30
Nest Js 첫걸음  (0) 2020.01.28
Nest js 소개  (0) 2020.01.27
블로그 이미지

_김은찬

두번 다시는 꺾이지 않으리

,