심심한 개발자의 취미생활

Provider

  • ProviderNestJS 애플리케이션의 기본적인 구성 요소이며, 의존성 주입(Dependency Injection) 이라는 강력한 디자인 패턴을 통해 서로 연결되고 관리 된다.
  • ProviderService, Repository, Factory, Helper 등과 같은 기본적인 Nest 클래스를 의미한다.
    • Provider의 가장 중요한 특징은 의존성으로 주입될 수 있다는 것으로 이를 통해 다양한 객체들이 서로 관계를 맺고 협력할 수 있다.

Provider 정의 : @Injectable()

  • 클래스를 프로바이더로 만들려면 클래스 선언부 위에 @Injectable() 데코레이터를 붙여야 한다. 본 데코레이터는 해당 클래스가 Nest IoC 컨테이너에 의해 관리 될 수 있음을 표시하는 메타데이터를 첨부한다.
// 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;
  }
}
import { Injactable } from 

의존성 주입 (Dependency Injection)

  • 의존성 주입 (Dependency Injectaion)이란, 한 객체가 다른 객체를 사용하는(의존하는) 관계에서, 필요한 객체를 내부에서 직접 생성하는 것이 아니라 외부(컨테이너)로 부터 전달받아 사용하는 디자인 패턴이다.
  • Provider의 가장 큰 장점은 의존성 주입으로, NestJS는 생성자 기반 의존성 주입을 사용한다.
  • 컨트롤러의 생성자에서 필요한 서비스의 타입을 지정하기만 하면, Nest가 알아서 해당 서비스의 인스턴스를 주입해준다.
// cats.controller.ts
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 {
    // 생성자에서 CatsService를 주입받는다.
    // private 접근자를 사용하면 멤버 변수로 선언과 초기화가 동시에 이루어진다.
   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 배열에 추가하면 된다.
// app.module.ts
import { Module } from '@nestjs/common'
import { CatsController } from './cats/cats.controller'
import { CatsService } from './cats/cats.service'

@Module({
    controller: [CatsController],
    providers: [CatsService] // CatsService를 이 모듈의 프로바이더로 등록
})
export class AppModule {}
  • 위와 같이 프로바이더를 등록하면 AppModule에 속한 모든 컴포넌트(CatsController)는 CatsService를 주입받을 수 있게 된다.
  • 만약 다른 모듈에서도 CatsService를 사용하고 싶다면, AppModuleexports 배열에 CatsService를 추가하여 다른 모듈에서 AppModuleimports하여 사용할 수 있다.

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() 데코레이터를 사용하여 특정 프로바이더를 속성에 주입할 수 있다.
  • 이 방법은 테스트가 어려워지는 등 단점이 있어 일반적으로는 생성자 주입이 권장된다.