Provider

장현욱(Artlogy)·2022년 10월 7일
0

Nest.js

목록 보기
3/18
post-thumbnail
post-custom-banner

컨트롤러는 요청을 알맞은 기능에 매칭해주고 알맞는 응답을 처리하는 역할을 가진다.
여기서 알맞은 기능을 담당하는 것이 프로바이더이다.

프로바이더에는 주로 서비스(Service), 레포지토리(Repository), 팩토리(Factory)등의 형태가 있다.

등록과 사용

프로바이더 등록


@Module({
    ...
  providers: [UsersService]
})
export class UsersModule {}

프로바이더 인스턴스는 모듈에 등록해주면 된다.

Nest개발자들 중 오해하는게 사람이 좀 있는데, @Injectable은 이 클래스가 Provider(공급자)라는걸 명시하고 스코프를 지정하기 위해서 쓰이는 데코레이터이다. 인스턴스화는 모듈에 등록을 해야 이루어 지는것이다.

프로바이더 사용


constructor(private usersService: UsersService) {}

위 처럼 생성자에서 의존성을 주입하여 사용하면된다.

프로바이더 스코프

프로바이더는 일반적으로 애플리케이션 스코프와 동기화된 스코프를 가진다.
즉 요청별 캐싱, 요청 추적 및 다중 테넌시처럼 스코프를 정해 동작시켜야 할 케이스가 있을때
어떻게 하는지 알아보자.

솔직히 몰라도 된다. 걍 이런게 있다는 것만 알고 자세한 내용은 여기서 살펴보자.

DEFAULT ( Singleton Instance )


@Injectable({scope:Scope.DEFAULT})

단일 인스턴스이며 애플리케이션 전체 범위에서 공유된다.
애플리케이션이 종료되면 같이 해제되고, 애플리케이션이 부트스트랩(실행)되면 인스턴스화된다.
즉 앱의 시작과 끝을 같이하는 생명주기와 애플리케이션 전체가 스코프 범위라고 볼 수 있다.

Nest 공식사이트에선 Default 즉 싱글톤 인스턴스로 프로바이더를 설정하는걸 가장 추천하고 안전하다고 말한다.

REQUEST


@Injectable({scope:Scope.REQUEST})

//커스텀 프로바이더경우
{
  provide: 'CACHE_MANAGER',
  useClass: CacheManager,
  scope: Scope.TRANSIENT,
}

들어오는 요청마다 인스턴스가 생성되고 요청을 처리하면 해제된다.
사용자의 요청은 단일이 아니라 여러가지 형태가 있을 수 있기때문에 이런게 있다고 이해하면된다.

import { Injectable, Scope, Inject } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';

@Injectable({ scope: Scope.REQUEST })
export class CatsService {
  constructor(@Inject(REQUEST) private request: Request) {}
}

TRANSIENT


@Injectable({scope:Scope.TRANSIENT})

이 스코프를 지정한 인스턴스는 공유되지 않는다. 임시(TRANSIENT) 프로바이더를 주입하는 각 컴포넌트는 새로 생성된 전용 인스턴스를 주입받게됩니다. 나는 이걸 써본적이 한번도 없는데,
선배는 정의만 해두고 쓰지않을때 주석대신(?)쓰는걸 보긴했다.

커스텀 프로바이더

프로바이더는 다양한 기능을 정의하고
다양한 기능 만큼 다양한 방식으로 프로바이더를 설정해야 할 때가 있다.
그 방법을 알아보겠다.

밸류 프로바이더


ValueProvider는 provideruseValue속성을 가진다. useValue는 어떤 타입도 받을 수 있기 때문에 useValue구문을 이용하여 외부 라이브러리로 부터 프로바이더를 삽입하거나 실제 구현을 모의 객체로 대체할 수 있다.

// 모의 객체 선언
const mockCatsService = {
  
};

@Module({
  imports: [CatsModule],
  providers: [
    {
      provide: CatsService,	
      useValue: mockCatsService,		
    },
  ],
})
export class AppModule {}

프로바이더 토큰


CatsRepository에서 데이터베이스에 연결하기 위해 Connection 객체를 프로바이더로 제공한다고 하면 다음과 같이 임의의 문자열로 선언할 수 있다. 다음 예에서는 'CONNECTION'을 토큰으로 사용한다.

import { connection } from './connection';

@Module({
  providers: [
    {
      provide: 'CONNECTION',
      useValue: connection,
    },
  ],
})
export class AppModule {}

CatsRepository에서 정의한 토큰으로 주입받을 수 있다.

@Injectable()
export class CatsRepository {
  constructor(@Inject('CONNECTION') connection: Connection) {}
}

클래스 프로바이더


밸류 프로바이더는 useValue 속성을 사용했지만, 클래스 프로바이더는 useClass 속성을 사용한다. 프로바이더를 동적으로 구성하고 싶을때 사용한다.

const configServiceProvider = {
  provide: ConfigService,
  useClass:
    process.env.NODE_ENV === 'development'
      ? DevelopmentConfigService
      : ProductionConfigService,
};

@Module({
  providers: [configServiceProvider],
})
export class AppModule {}

팩토리 프로바이더 (Factory Provider)


팩토리 프로바이더는 클래스 프로바이더와 같이 동적으로 구성하고자 할 때 사용하지만,
앞서와는 다르게 타입이 함수로 정의되어 있다.

const connectionFactory ={
  provide: 'ASYNC_CONNECTION',
  useFactory: async () => {
    const connection = await createConnection(options);
    return connection;
  },
}

@Module({
  providers: [connectionFactory],
})
export class AppModule {}

메소드를 정의하는 과정에서 다른 프로바이더가 필요하다면 사용 할 수 있는데,
Inject속성을 통해 정의를 해주면 된다.

const connectionFactory = {
  provide: 'CONNECTION',
  useFactory: (optionsProvider: OptionsProvider) => {
    const options = optionsProvider.get();
    return new DatabaseConnection(options);
  },
  inject: [OptionsProvider],
};

@Module({
  providers: [connectionFactory],
})
export class AppModule {}

프로바이더 Import&Export

Export


다른 모듈에 있는 프로바이더를 쓸려면 해당 모듈에서 내보내기를 해줘야한다.

const connectionFactory = {
  provide: 'CONNECTION',
  useFactory: (optionsProvider: OptionsProvider) => {
    const options = optionsProvider.get();
    return new DatabaseConnection(options);
  },
  inject: [OptionsProvider],
};

@Module({
  providers: [connectionFactory],
  exports: [connectionFactory],
})
export class AppModule {}

Import


해당 모듈범위에서 외부 프로바이더를 가져와 쓰고싶다면 imports를 이용하면 된다.

const connectionFactory = {
  provide: 'CONNECTION',
  useFactory: (optionsProvider: OptionsProvider) => {
    const options = optionsProvider.get();
    return new DatabaseConnection(options);
  },
  inject: [OptionsProvider],
};

@Module({
  imports:[HelloService],
  providers: [connectionFactory],
  exports: [connectionFactory],
})
export class AppModule {}
post-custom-banner

0개의 댓글