[공식문서] Providers

Haeseo Lee·2023년 4월 7일
0

NestJS 공식문서

목록 보기
2/4
post-thumbnail

공식문서 공부하기
https://docs.nestjs.com/providers

필요한 부분만 발췌해서 정리

Provider 정의

Provider는 Nest의 핵심 개념으로, 대부분의 Nest class들(e.g., service, repository, factory, helper, etc..)이 provider로 취급된다. Provider의 핵심은 의존성으로 주입이 가능하다는 것으로, 객체들이 서로 간에 다양한 관계를 만들 수 있다는 것이다.

Controller는 단순히 HTTP 요청을 처리하는 일을 하고 복잡한 비즈니스 로직은 Provider에서 처리하도록 해야 한다. provider 역시도 class이며, module에서 providers 안에 등록된다.

Services

만든 provider가 다른 곳에서 사용될 수 있으려면 @Injectable 데코레이터를 사용해야 한다. @Injectable() 데코레이터는 해당 class가 Nest IoC 컨테이너에 의해 관리되는 class임을 선언해준다.

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

controller의 생성자에서 service를 private으로 선언하면, 해당 service를 controller의 property로서 바로 초기화하게 된다.

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

  ...
}

Dependency Injection

Nest는 'Dependency Injection(의존성 주입)'이라는 디자인 패턴 위에 설계되었고, Typescript의 특성 덕에 type을 이용하여 의존성을 관리한다. 아래의 코드처럼, Nest는 catsService를 CatsService의 인스턴스를 생성하고 반환함으로써 resolve해준다.
(resolve를 번역할 적절한 단어가 뭔지 모르겠다. 연결? 엮는다?).

constructor(private catsService: CatsService) {}

Scopes

보통 provider의 수명(scope)은 application의 생명주기와 같다. application이 bootstrap될 때 모든 의존성들도 반드시 연결되어야 하고, 따라서 모든 provider들이 인스턴스화되어야 한다. 그러므로 application이 꺼질 때, 모든 provider(인스턴스)들도 없어지게 되는 것이다.
그러나 필요하다면 provider의 수명을 request-scoped로 설정할 수 있다.

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

@Injectable({ scope: Scope.REQUEST })
export class CatsService {}

Optional providers

모든 의존성들이 반드시 연결되어야 할 필요가 없을 때가 있다. 예를 들어, 어떤 class가 특정 configuration에 의존하고, none이 넘어왔을 때 기본값을 사용해야 한다면, 의존성은 optional해야 한다.

provider를 optional하게 만드려면, @Optional() 데코레이터를 provider 생성자의 매개변수에 넣어준다.

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

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

Property-based injection

위에서 사용하던 방식은 생성자에 의존성을 주입하는 방식이었다.
특정 경우에서는 property에 의존성을 주입하는 방식을 사용할 수 있다.

예를 들어, 여러 개의 provider에 의존하는 상위 클래스가 있고, 서브 클래스에서 그 provider들을 super()를 통해 죄다 호출하는 걸 피하고 싶을 때 사용할 수 있다.

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

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

그러나 class가 어떤 provider를 상속하는 게 아닌 이상, 생성자 주입 방식을 사용하는 게 좋다.

Provider 등록

provider(e.g. service)를 만들었고 그 provider를 사용하는 소비자(e.g. controller)가 생겼다면, 만든 provider를 Module안에 등록해줘야 한다. 그래야 Nest가 controller 내의 의존성들을 엮어줄 수 있다.

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 {}
profile
잡생각 많은 인간

0개의 댓글