Providers
프로바이더는 Nest의 기본 개념이다. 서비스, 리포지토리, 팩토리, 헬퍼 등 많은 기본 Nest 클래스가 프로바이더로 취급될 수 있다. 프로바이더의 주요 개념은 종속성으로 주입될 수 있다는 것이다. 즉, 각각의 객체가 다양한 관계를 생성할 수 있으며, 이러한 객체를 "wiring up(배선)"하는 기능은 대부분 Nest 런타임 시스템에 위임할 수 있다.
이전 챕터에서 간단한 CatsControllers를 구축했다. 컨트롤러는 HTTP 요청을 처리하고 더 복잡한 작업을 프로바이더에 위임해야 한다. 프로바이더는 모듈에서 프로바이더로 선언되는 일반 자바스크립트 클래스이다.
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() 데코레이터를 사용한다는 것이다. 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는 클래스 생성자를 통해 주입된다. private 구문을 사용하는 것을 주목하자. private를 사용하면 catsServices 멤버를 즉시 선언하고 초기화할 수 있다. (타입스크립트의 일종의 편의성 문법이다.)
Dependency injection
Nest는 일반적으로 의존성 주입이라고 알려진 강력한 디자인 패턴을 기반으로 구축되었다. 이 개념에 대한 자세한 내용은 Angular 공식문서에서 읽어볼 수 있다.
Nest에서는 타입스크립트 기능 덕분에 의존성을 매우 쉽게 관리할 수 있다. 아래 예제에서 Nest는 CatsService의 인스턴스를 생성하고 반환함으로써(또는 싱글톤의 일반적인 경우 다른 곳에서 이미 요청된 경우 기존 인스턴스를 반환함으로써) catsService를 resolve한다. 즉 의존성은 resolve 되어 컨트롤러의 생성자에 전달되거나 지정된 프로퍼티에 할당된다.
constructor(private catsService: CatsService) {}
Scopes
프로바이더는 일반적으로 애플리케이션 수명 주기와 동기화된 라이프사이클을 가진다. 애플리케이션이 부트스트랩되면 모든 종속성이 resolve되어야 하므로 모든 프로바이더가 인스턴스화되어야 한다. 마찬가지로 애플리케이션이 종료되면 프로바이더들은 삭제된다. 하지만 프로바이더에 request-scoped 생명주기를 부여할 수도 있다. 이 기술은 FUNDAMENTAL 파트에서 다룬다.
Custom providers
Nest에는 프로바이더 간의 관계를 해결하는 제어 역전(IoC) 컨테이너가 내장되어 있다. 이 기능은 위에서 설명한 의존성 주입 기능의 기반이 되지만, 사실 지금까지 설명한 것보다 훨씬 강력하다. 공급자를 정의하는 방법에는 일반값, 클래스, 비동기 또는 동기 팩토리를 사용할 수 있는 여러가지 방법이 있다. 이 기술은 FUNDAMENTAL 파트에서 다룬다.
Optional providers
때로는 반드시 resolve 될 필요가 없는 의존성도 있을 수 있다. 예를 들어 클래스가 구성 객체에 의존될 수 있지만 아무것도 전달되지 않으면 기본값을 사용해야 한다. 이러한 경우 설정 프로바이더가 없어도 오류가 발생하지 않으므로 의존성은 선택 사항이 된다.
프로바이더가 선택사항임을 나타내려면 생성자 시그니처에 @Optional() 데코레이터를 사용한다.
import { Injectable, Optional, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}
위의 예시에서는 사용자 정의 프로바이더를 사용하고 있으며, 이것이 바로 HTTP_OPTIONS 사용자 정의 토큰을 사용하는 이유이다. 이전 예제에서는 생성자의 클래스를 통해 의존성을 나타내는 생성자 기반 인젝션을 보여주었다. 이 기술은 FUNDAMENTAL 파트에서 다룬다.
Property-based injection
지금까지 사용한 기술은 생성자 메서드를 통해 프로바이더를 주입하기 때문에 생성자 기반 주입이라고 한다. 매우 특정한 경우에는 프로퍼티 기반 주입이 유용할 수 있다. 예를 들어 최상위 클래스가 하나 또는 여러개의 프로바이더에 의존하는 경우 생성자에서 하위 클래스에서 super()를 호출하여 모든 프로바이더를 전달하는 것은 매우 지루할 수 있다. 이를 방지하기 위해 프로퍼티 수준에서 @Inject() 데코레이터를 사용할 수 있다.
import { Injectable, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
@Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
Provider registration
이제 프로바이더(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는 CatsController 클래스의 종속성 문제를 해결할 수 있다. 디렉토리 구조는 아래와 같을 것이다.
'NestJS' 카테고리의 다른 글
Modules (0) | 2024.04.17 |
---|---|
Controllers (0) | 2024.04.17 |
First steps (0) | 2024.04.17 |
Introduction (0) | 2024.04.17 |