처음부터 차근차근

[NestJS] Providers 본문

FrameWork/NestJS

[NestJS] Providers

HangJu_95 2023. 11. 24. 22:22
728x90

Provider란?

Provider(제공자)는 Nest의 기본 개념이다.

  • Service, Repository, helper, factory 등 많은 기본 Nest Class들이 Provider로 취급될 수 있다.
  • Provider는 의존성을 주입할 수 있다.(객체가 서로 다양한 관계를 만들 수 있으며, 이를 해주는 역할을 NestJS의 런타임이 맡고 있다.)
  • 대표적으로 Controller에 요청을 반는 핸들러를 선언하고, 복잡한 로직은 Provider에 위임한다.
  • Nest는 객체지향적은 의존관계를 설정할 수 있기 때문에 SOLID 원칙을 따르는 것을 권장한다. 

Service

간단한 Service 로직을 만들어보자. 

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() 데코레이터는 metadata를 사용하여 CatsService가 Nest IoC 컨테이너에서 관리할 수 있는 클래스임을 선언해준다.

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 {
  // 컨트롤러 클래스 생성자를 통해 catsService 주입
  constructor(private catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}

여기서 클래스 생성자를 통해 catsService를 주입하였다.

여기서 주목할 점은 생성자 파라미터이다. 보통 자바에서 객체의 생성자는 아래와 같다.

public class User {
  private String name;
  constructor(String name) {
    this.name = name;
  }
}

타입 스크립트는 자바보다 훨씬 간단하게 생성자를 생성할 수 있다. 아래 코드는 위의 코드와 동일한 동작을 한다.

class User {
  constructor(private name: string) {}
}

의존성을 주입하기 위한 방법은 크게 3가지가 있다.

  • 생성자를 이용한 의존성 주입(Constructor Injection)
  • 수정자를 이용한 의존성 주입(Setter Injection)
  • 필드를 이용한 의존성 주입(Field Injection)

Nest에서는 주로 생성자를 이용한 의존성 주입을 권장하며, 다른 방법도 존재한다.

Nest에서는 Typescript 덕분에 타입을 통해 객체를 찾아서 의존성 주입을 해주며, 다른 곳에서 사용한 적이 있는 클래스일 경우 singleton 객체를 주입해줌

Scopes

프로바이더는 일반적으로 Nest 프로그램의 수명주기와 동기화 된 수명(범위)를 갖는다.

Nest application이 BootStapped되면(시작되면), 모든 종속성을 해결해야 하기 때문에 모든 Provider가 인스턴스화 되며, 마찬가지로 app을 종료할 경우 각 Provider가 메모리에서 삭제된다.

그러나 Provider의 수명을 요청 단위로 제한하는 방법도 있지만, 성능에 문제가 될 수 있기 때문에 특수한 상황이 아닌 경우 기본 설정된 수명 주기를 사용하는 것이 좋다.

선택적 providers

생성자 파라미터에 @Optional() 데코레이터를 사용해서 의존 관계가 필수가 아님을 선언할 수 있다.

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

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

때때로, 반드시 해결될 필요가 없는 종속성이 있을 수 있다. 예를 들어 configuration 객체에 의존할 수 있지만 해당 인스턴스가 없는 경우 기본값을 사용하는 경우이다.

Property-based injection

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

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

@Inject decorator를 사용해서 생성자 파라미터를 주입받지 않고 프로퍼티로 직접 주입시킬 수 있다.

Provider가 또 다른 Provider를 의존하는 관계에서는 super()를 호출해서 주입받은 인스턴스를 부모에게 올려보내야 하는데 이런 상황에서 사용할 수 있음.

그러나 클래스가 다른 프로바이더를 확장하지 않는 이상 생성자를 이용한 의존성 주입을 권장한다.

 

Provider registration

import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';

@Module({
  controllers: [CatsController],
  // 등록할 Provider를 적는다.
  providers: [CatsService],
})
export class AppModule {}

Provider 또한 Controller처럼 Module에 등록해줘야 한다.

참고

https://docs.nestjs.com/providers

https://www.wisewiredbooks.com/nestjs/overview/04-provider.html

https://gongmeda.tistory.com/48

 

 

'FrameWork > NestJS' 카테고리의 다른 글

[NestJS] Middleware  (0) 2023.11.25
[NestJS] Request Lifecycle  (0) 2023.11.25
[NestJS] Module  (1) 2023.11.24
[NestJS] Controller  (1) 2023.11.24
[NestJS] NestJS는 왜 만들어졌을까??  (1) 2023.11.13