Provider
Provider는 NestJS 애플리케이션의 기본적인 구성 요소이며, 의존성 주입(Dependency Injection) 이라는 강력한 디자인 패턴을 통해 서로 연결되고 관리 된다.
Provider는 Service, Repository, Factory, Helper 등과 같은 기본적인 Nest 클래스를 의미한다.
Provider의 가장 중요한 특징은 의존성으로 주입될 수 있다는 것으로 이를 통해 다양한 객체들이 서로 관계를 맺고 협력할 수 있다.
Provider 정의 : @Injectable()
- 클래스를 프로바이더로 만들려면 클래스 선언부 위에
@Injectable() 데코레이터를 붙여야 한다. 본 데코레이터는 해당 클래스가 Nest IoC 컨테이너에 의해 관리 될 수 있음을 표시하는 메타데이터를 첨부한다.
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;
}
}
import { Injactable } from
의존성 주입 (Dependency Injection)
의존성 주입 (Dependency Injectaion)이란, 한 객체가 다른 객체를 사용하는(의존하는) 관계에서, 필요한 객체를 내부에서 직접 생성하는 것이 아니라 외부(컨테이너)로 부터 전달받아 사용하는 디자인 패턴이다.
Provider의 가장 큰 장점은 의존성 주입으로, NestJS는 생성자 기반 의존성 주입을 사용한다.
- 컨트롤러의 생성자에서 필요한 서비스의 타입을 지정하기만 하면, Nest가 알아서 해당 서비스의 인스턴스를 주입해준다.
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
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();
}
}
Provider 등록
@Injectable()로 정의된 프로바이더는 특정 모듈(Module)에 등록되어야 Nest 전체 애플리케이션에서 인식하고 관리 할 수 있으며 @Module() 데코레이터 안의 providers 배열에 추가하면 된다.
import { Module } from '@nestjs/common'
import { CatsController } from './cats/cats.controller'
import { CatsService } from './cats/cats.service'
@Module({
controller: [CatsController],
providers: [CatsService]
})
export class AppModule {}
- 위와 같이 프로바이더를 등록하면
AppModule에 속한 모든 컴포넌트(CatsController)는 CatsService를 주입받을 수 있게 된다.
- 만약 다른 모듈에서도
CatsService를 사용하고 싶다면, AppModule의 exports 배열에 CatsService를 추가하여 다른 모듈에서 AppModule을 imports하여 사용할 수 있다.
Provider Scopes
- 프로바이더는 인스턴스가 생성되고 공유되는 방식에 따라 다른
스코프 (Scope)를 가질 수 있다.
- 싱글톤 (Singleton, default)
- 애플리케이션이 시작될 때 프로바이터의 인스턴스가 하나만 생성되고, 애플리케이션이 실행되는 동안 해당 인스턴스가 계속 공유된다.
- 요청 스코프 (Request-scoped)
- 들어오는 각 요청(Request)마다 프로바이터의 새로운 인스턴스가 생성되며, 요청 처리가 끝나면
Garbage Collector에 의해 수거된다.
- 사용자별 데이터 캐싱 등 특정 요청에 한정된 상태를 관리할 때 유용하다.
- 트랜지언트 (Transient)
- 프로바디어틀 주입 받는 각 컴포넌트마다 새로운 인스턴스가 생성된다. 즉 여러 컴포넌트가 동일한 트랜지언트 프로바이더를 주입 받아도 모두 다른 인스턴스를 갖게 된다.
커스텀 프로바이더 (Custom Provider)
NestJS는 클래스 자체를 프로바이더로 사용하는 것 외에도, 더 유연한 방법으로 프로바이더를 정의할 수 있는 Custom Provider 기능을 제공한다.
- 이는 일반 값, 기존 클래서, 또는 동적 팩토리 함수를 프로바이더로 사용하고 싶을 때 유용하다.
useValue : 특정 상수 값(설정 객체 등)을 주입 하고 싶을 때 사용
useClass : 특정 조건에 따라 다른 클래스를 동적으로 주입 하고 싶을 때 사용
useFactory : 다른 프로바이더를 조합하거나 비동기 작업이 필요한 복잡한 객체를 생성하여 주입하고 싶을 때 사용
선택적 프로바이더 (Optional Provider)
- 때로는 특정 의존성이 반드시 존재하지 않아도 되는 경우가 있는데, 이럴 때 생성자에서
@Optional() 데코레이터를 사용하면, 해당 프로바이더가 등록되어 있지 않아도 Nest가 오류를 발생시키지 않고 undefined를 주입한다.
속성 기반 주입 (Property-based Injection)
- 생성자 주입이 가장 일반적이지만, 클래스의 속성(Property)에 직접 의존성을 주입할 수 있는데,
@inject() 데코레이터를 사용하여 특정 프로바이더를 속성에 주입할 수 있다.
- 이 방법은 테스트가 어려워지는 등 단점이 있어 일반적으로는 생성자 주입이 권장된다.