3. Providers
프로바이더는 Nest의 핵심 개념입니다. 서비스, 레포지토리, 팩토리, 헬퍼와 같은 많은 기본 Nest 클래스는 프로바이더로 취급될 수 있습니다. 프로바이더의 핵심 아이디어는 객체가 서로 다양한 관계를 형성할 수 있도록 종속성으로 주입할 수 있다는 것입니다. 이러한 객체를 "연결"하는 책임은 주로 Nest 런타임 시스템에서 처리합니다.
이전 장에서는 간단한 CatsController를 만들었습니다. 컨트롤러는 HTTP 요청을 처리하고 더 복잡한 작업을 프로바이더에 위임해야 합니다. 프로바이더는 NestJS 모듈에서 프로바이더로 선언된 일반 자바스크립트 클래스입니다. 자세한 내용은 "모듈" 장을 참조하세요.
Hint.
Nest를 사용하면 종속성을 객체 지향 방식으로 설계하고 구성할 수 있으므로 SOLID 원칙을 따르는 것을 적극 권장합니다.
Services
간단한 CatsService를 만드는 것으로 시작하겠습니다. 이 서비스는 데이터 저장 및 검색을 처리하고 CatsController에서 사용할 것입니다. 애플리케이션의 로직을 관리하는 역할을 하기 때문에 공급자로 정의하기에 이상적인 후보입니다.
// 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() 데코레이터입니다. 이 데코레이터는 클래스에 메타데이터를 첨부하여 CatsService가 Nest IoC 컨테이너에서 관리할 수 있는 클래스임을 알립니다. 또한 이 예제에서는 다음과 같이 보이는 Cat 인터페이스를 사용합니다:
// interfaces/cat.interface.ts
export interface Cat {
name: string;
age: number;
breed: string;
}
이제 고양이를 검색하는 서비스 클래스가 생겼으니 CatsController 내부에서 사용해 보겠습니다:
// 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는 클래스 생성자를 통해 주입됩니다. 비공개 키워드가 사용된 것을 주목하세요. 이 단축키를 사용하면 같은 줄에서 catsService 멤버를 선언하고 초기화할 수 있으므로 프로세스가 간소화됩니다.
Dependency injection
Nest는 의존성 주입이라는 강력한 디자인 패턴을 기반으로 구축되었습니다. 공식 Angular 문서에서 이 개념에 대한 훌륭한 글을 읽어보시기를 적극 권장합니다.
Nest에서는 TypeScript의 기능 덕분에 종속성이 유형에 따라 해결되므로 종속성 관리가 간단합니다. 아래 예제에서 Nest는 CatsService의 인스턴스를 생성하고 반환하여(또는 싱글톤의 경우 다른 곳에서 이미 요청된 경우 기존 인스턴스를 반환하여) catsService를 해결합니다. 그런 다음 이 종속성은 컨트롤러의 생성자에 주입되거나 지정된 프로퍼티에 할당됩니다:
constructor(private catsService: CatsService) {}
Scopes
프로바이더는 일반적으로 애플리케이션 수명 주기와 일치하는 수명("범위")을 갖습니다. 애플리케이션이 부트스트랩되면 각 종속성이 해결되어야 하며, 이는 모든 공급자가 인스턴스화됨을 의미합니다. 마찬가지로 애플리케이션이 종료되면 모든 프로바이더가 소멸됩니다. 그러나 프로바이더의 수명이 애플리케이션의 수명 주기가 아닌 특정 요청에 묶여 있도록 요청 범위로 프로바이더를 만들 수도 있습니다. 이러한 기술에 대한 자세한 내용은 주입 범위 장에서 확인할 수 있습니다.
Custom Providers
Nest에는 공급자 간의 관계를 관리하는 IoC("제어의 역전") 컨테이너가 내장되어 있습니다. 이 기능은 의존성 주입의 기초가 되는 기능이지만 실제로는 지금까지 다룬 것보다 훨씬 더 강력합니다. 공급자를 정의하는 방법에는 일반 값, 클래스, 비동기 또는 동기 팩토리를 모두 사용할 수 있는 등 여러 가지가 있습니다. 프로바이더 정의에 대한 더 많은 예제는 의존성 주입 챕터를 참조하세요.
Optional Providers
때로는 항상 해결할 필요가 없는 종속성이 있을 수 있습니다. 예를 들어 클래스가 구성 객체에 종속되어 있지만 제공되지 않은 경우 기본값을 사용해야 할 수 있습니다. 이러한 경우 종속성은 선택 사항으로 간주되며 구성 공급자가 없어도 오류가 발생하지 않습니다.
제공자를 선택 사항으로 표시하려면 생성자 서명에서 @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 (속성 기반 주입)
지금까지 사용한 기술을 생성자 기반 주입이라고 하며, 생성자 메서드를 통해 프로바이더를 주입합니다. 특정 특정 경우에는 프로퍼티 기반 주입이 유용할 수 있습니다. 예를 들어 최상위 클래스가 하나 이상의 프로바이더에 의존하는 경우 하위 클래스에서 super()를 통해 모든 프로바이더를 전달하면 번거로울 수 있습니다. 이를 방지하려면 속성 수준에서 @Inject() 데코레이터를 직접 사용할 수 있습니다.
import { Injectable, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
@Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
경고:
클래스가 다른 클래스를 확장하지 않는 경우 일반적으로 생성자 기반 주입을 사용하는 것이 좋습니다. 생성자는 필요한 종속성을 명확하게 지정하므로 @Inject로 주석을 단 클래스 프로퍼티에 비해 더 나은 가시성을 제공하고 코드를 더 쉽게 이해할 수 있습니다.
Provider registeration (프로바이더 등록)
이제 공급자(CatsService)와 소비자(CatsController)를 정의했으므로, 서비스를 Nest에 등록하여 인젝션을 처리할 수 있도록 해야 합니다. 이 작업은 모듈 파일(app.module.ts)을 편집하고 @Module() 데코레이터의 공급자 배열에 서비스를 추가하여 수행합니다.
// 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는 CatController 클래스의 종속성을 해결할 수 있습니다.
이 시점에서 폴더구조는 다음과 같아야합니다.
Manual instantiation
지금까지 Nest가 종속성 해결을 위한 대부분의 세부 사항을 자동으로 처리하는 방법에 대해 살펴보았습니다. 그러나 경우에 따라 기본 제공 종속성 주입 시스템에서 벗어나 수동으로 공급자를 검색하거나 인스턴스화해야 할 수 도 있습니다. 이러한 기술 두가지를 아래에 간략하게 설명합니다.
- 기존 인스턴스를 검색하거나 프로바이더를 동적으로 인스턴스화하려면 모듈 참조를 사용할 수 있습니다.
bootstrap()
함수 내에서 공급자를 가져오려면 (예: 독립형 애플리케이션의 경우 또는 부트스트랩 중 구성 서비스 사용) 독립형 애플리케이션을 확인하세요.