https://docs.nestjs.com/providers
대부분의 Nest 클래스는 프로바이더임(services, repositories, factories, helpers, etc.)
프로바이더의 메인 아이디어는 dependency로써 inject, 주입 될 수 있다는 것이다; 이것은 객체들이 서로 다양한 관계를 맺을 수 있고, 객체의 인스턴스들을 “wiring up”하는 기능은 Nest 런타임 시스템에 위임될 수 있다.
컨트롤러는 HTTP 요청을 처리하고 그보다 복잡한 작업은 프로바이더로 위임해야함
프로바이더는 모듈 내의 providers로 선언된 자바스크립트 클래스이다.
Nest는 의존성을 OO(?객체지향인듯) 방식으로 설계할 수 있으므로 SOLID 원칙을 따르는 것이 좋다.
SOLID
1. Single responsibility principle: 한 클래스는 하나의 책임
2. Open-closed principle: 확장에는 열리고 변경에는 닫힘
3. Liskov substitution principle: 객체는 하위 타입의 인스턴스로 바꿀 수 있어야 함
4. Interface segregation principle: 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다
5. Dependency Inversion principle: 추상화에 의존해야..(의존성 주입이 이 원칙을 따름)
CatsService
를 만들어보자.
해당 서비스는 데이터 저장과 검색에 책임이 있고 CatsController
에서 사용하도록 디자인 되었기 때문에, 프로바이더로 정의되기 좋음?
해당 데코레이터는 Nest IoC 컨테이너에서 관리할 수 있는 클래스임을 나타냄
constructor(private catsService: CatsService) {}
CatsService는 클래스 생성자를 통해 주입된다.
private을 사용한 걸 잘 보세용
Nest는 의존성 주입이라고 알려진 강력한 디자인 패턴에 입각해서 만들어졌다.
의존성은 클래스가 기능을 수행하는 데 필요한 서비스 또는 객체이다. DI(Dependency Injection)는 클래스가 (외부 소스에서 의존성을 생성하지 않고) 의존성을 요청하는 설계 패턴이다. from Angular documentation
recommend reading a great article about this concept in the official Angular documentation.
Nest에선 타입스크립트 덕분에 의존성을 관리하는 것이 엄청나게 쉬워졌다. 왜냐? 타입만으로 리졸브댐. (타입만으로 해결된다는게??)
constructor(private catsService: CatsService) {}
위 코드를 보면 Nest가 catsService를 resolve함(CatsService의 인스턴스를 생성하고 리턴함으로써) (또는 싱글톤의 일반적인 예시처럼, 만약 다른 곳에서 요청이 있었다면 이미 존재하는 인스턴스를 리턴함으로써)
이 의존성은 리졸브되어(해결되어라고하기뭔가이상) 컨트롤러의 생성자로 전달된다. (표시된 프로퍼티에 할당됨)
프로바이더는 보통 (어플리케이션 생명주기와 동기화된) 생명주기(”scope”)를 가진다. 어플리케이션이 부트스트랩되면(?) 모든 의존성들은 리졸브 되어야 하고, 따라서 모든 프로바이더들은 인스턴스화 되어야 한다. 비슷하게, 어플리케이션이 죽으면 각 프로바이더들은 파괴된다.
그러나 프로바이더 생명주기를 request-scoped로 만들 수도 있다. (You can read more about these techniques here.)
Nest는 빌트인 inversion of control(”IoC”)를 가진다.
프로바이더를 정의하는 방법
가끔, 반드시 리졸브 될 필요가 없는 의존성이 있을 수 있음
예를들어
너의 클래스가 configuration object에 의존하고 있지만, 아무것도 넘어오지 않았으면 디폴트 값을 사용해야 한다. (?)
이런 상황에서 의존성은 옵셔널이 됨(because lack of the configuration provider wouldn't lead to errors.)
프로바이더가 옵셔널임을 알려주려면 위 데코레이터를 생성자 시그니처에 사용하면 된다.
import { Injectable, Optional, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}
Note that in the example above we are using a custom provider, which is the reason we include the
HTTP_OPTIONS
custom token.(?) Previous examples showed constructor-based injection indicating a dependency through a class in the constructor. Read more about custom providers and their associated tokens here.
지금까지 사용한 건 생성자를 통해 프로바이더가 주입되는 constructor-based injection이다.
특정 경우에선 property-based injection이 유용할 수도 있음
예를 들면,
top-level 클래스가 하나 이상의 프로바이더에 의존하고, passing them all the way up by calling super()
in sub-classes from the constructor can be very tedious.
이를 피하기 위해 프로퍼티 레벨에서 @Inject()
데코레이터를 사용할 수 있다.
import { Injectable, Inject } from '@nestjs/common';
@Injectable()
export class HttpService<T> {
@Inject('HTTP_OPTIONS')
private readonly httpClient: T;
}
프로바이더는 모듈에 등록을 하자