lottie
Seungjun's blog
blog
nestjs docs 4편

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


Getting up and running

   위의 컨트롤러가 완전히 정의된 상태에서 Nest는 여전히 CatsController가 존재하는지 알지 못하므로 클래스의 인스턴스를 생성하지 않습니다.

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

// app.module.ts

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

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

  위 예시 코드에서@Module()데코레이터 를 사용하여 모듈 클래스에 메타데이터를 첨부했고, Nest는 마운트해야 하는 컨트롤러를 쉽게 반영할 수 있습니다.


Library-specific approach

  특정 response에 object를 주입하려면 @Res()데코레이터를 사용해야 합니다.

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([]);
  }
}

  이 방식이 효과가 있고 실제로 response object의 전체 제어(헤더 조작, 라이브러리별 기능 등)가 가능해 더 많은 유연성을 지니지만 주의해야 합니다. 일반적으로 이 방식은 훨씬 덜 명확하고 몇 가지 단점이 있습니다. 가장 큰 단점은 코드가 플랫폼에 따라 달라지고(기본 라이브러리가 응답 객체에 대해 다른 API를 가질 수 있으므로) 테스트하기가 더 어렵다는 것입니다.

  또한 위의 예에서 인터셉터 및 @HttpCode() / @Header()데코레이터 와 같은 Nest 표준 응답 처리에 의존하는 Nest 기능과의 호환성이 손실됩니다 . 이 문제를 해결하려면 다음과 같이 passthrough옵션을 true로 설정할 수 있습니다 .

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

 이제 response object (예: 특정 조건에 따라 쿠키 또는 헤더 설정) 와 상호 작용할 수 있지만 나머지는 프레임워크에 맡깁니다.


Providers(인스턴스를 직접 생성)

    Providers는 Nest의 기본 개념입니다**.** Nest는 services, repositories, factories, helpers 등의 많은 기본 내장 클래스를 Providers로 다루고 있습니다. 

  Provider의 주요 아이디어는 종속성을 주입 할 수 있다는 것입니다. 즉, 개체는 서로 다양한 관계를 만들 수 있으며 개체 인스턴스를 "연결"하는 기능은 대부분 Nest 런타임 시스템에 위임될 수 있습니다.

image

 Controller는 HTTP 요청을 처리하고 보다 복잡한 작업을 provider 에게 위임해야 합니다. 

 providers는 모듈에서 선언되는 일반 JavaScript 클래스입니다 .


Services

  아래 service는 데이터 저장 및 검색을 담당하며 CatsController에서 사용하도록 설계된 provider 입니다.

// cats.service.ts

import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}

 CatsService는 하나의 속성과 두 개의 메서드가 있는 기본 클래스입니다. 유일한 새로운 기능은 @Injectable()데코레이터를 사용한다는 것입니다. @Injectable()데코레이터는 컨테이너에서 관리할 수 있는 클래스 임을 선언하는 메타데이터를 첨부합니다 . 이 메타데이터에는 CatsService가Nest IoC,@Injectable() 에서 관리되는 클래스라는점이 담겨져 있습니다.

// interfaces/cat.interface.ts

export interface Cat {
  name: string;
  age: number;
  breed: string;
}

 아래는 controller 에서 고양이를 검색하는 service class를 사용하는 예제 코드입니다.

// cats.controller.ts

import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}

 CatsService는 클래스 생성자를 통해 주입 됩니다 .


Dependency injection

   Nest는 일반적으로 종속성 주입 으로 알려진 강력한 디자인 패턴을 기반으로 구축되었습니다 .

 Nest에서는 TypeScript 덕분에 종속성 관리가 쉽습니다. 아래 예에서 Nest는 catsService인스턴스를 생성하고 반환하여 문제를 해결 합니다(또는 일반적인 싱글톤의 경우 이미 다른 곳에서 요청되어 생성된 기존 인스턴스를 반환합니다). 이 종속성은 해결되어 컨트롤러의 생성자에 전달됩니다(또는 표시된 속성에 할당됨).

constructor(private catsService: CatsService) {}


Scopes

    provider는 일반적으로 애플리케이션 라이프 사이클과 동기화된 수명("범위")을 갖습니다. 애플리케이션이 부트스트랩되면 모든 종속성을 해결해야 하므로 모든 공급자를 인스턴스화해야 합니다. 마찬가지로 애플리케이션이 종료되면 각 provider가 소멸됩니다. 그러나 provider의 수명을 요청 범위로 만드는 방법 도 있습니다.


Custom providers

   nest는 provider 간의 관계를 해결하는 내장된 제어 반전("IoC") 컨테이너를 갖고 있습니다. 이 기능은 위에서 설명한 의존성 주입 (dependency injection)기능의 기초이지만, 실제로는 훨씬 강력합니다. provider를 정의하는 방법에 단순한 값, 클래스 및 비동기 또는 동기 팩토리 사용 등이 있습니다.


Optional providers

    가끔씩, 해결되지 않아도 되는 종속성이 있을 수 있습니다. 예를 들어, 클래스가 configuration object에 의존할 수 있지만 전달하지 않으면 기본값을 사용해야 합니다. 이 경우, configuration provider의 부재로 인해 오류가 발생하지 않으므로 종속성은 선택적입니다.

 provider가 선택적임을 나타내려면 constructor's signature에 @Optional() 데코레이터를 사용하십시오.

import { Injectable, Optional, Inject } from '@nestjs/common';

@Injectable()
export class HttpService<T> {
  constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}

  위의 예제에서는 사용자 정의 프로바이더를 사용하고 있으며, 이것이 HTTP_OPTIONS 사용자 정의 토큰을 포함하는 이유입니다. 이전 예제에서는 클래스를 통해 종속성을 나타내는 생성자 기반 주입을 보여주었습니다.


Property-based injection

    지금까지 사용한 기술은 constructor-based injection이라고 불린다. constructor method로 providers가 주입된다. 매우 특별한 경우, property-based injection이 유용할 수 있다.

 예를 들어, 최상위 클래스가 하나 이상의 providers에 의존하는 경우, constructor에서 하위 클래스에서 super()를 호출하여 모든 제공자를 전달하는 것은 매우 지루할 수 있다. 이를 피하기 위해 property level에서 @Inject() 데코레이터를 사용할 수 있다.

import { Injectable, Inject } from '@nestjs/common';

@Injectable()
export class HttpService<T> {
  @Inject('HTTP_OPTIONS')
  private readonly httpClient: T;
}


Provider registration

   이제 우리는 provider(CatsService)를 정의하고 이 서비스의 소비자 (CatsController)가 있다면, Nest에 서비스를 등록하여 주입할 수 있도록 해야합니다. 이를 위해 모듈 파일 (app.module.ts)을 편집하여 @Module() 데코레이터의 providers 배열에 서비스를 추가합니다.

// app.module.ts

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

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

 이제 Nest는 CatsController 클래스의 의존성을 해결할 수 있습니다.


Manual instantiation

  Nest가 의존성 주입을 자동으로 처리하는 방법 중 일부의 경우에는 내장된 의존성 주입 시스템을 벗어나서 수동으로 provider를 검색하거나 인스턴스화해야 할 수 있습니다.