개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

NestJS Provider — DI 컨테이너가 관리하는 객체

2025년 2월 25일
NestJSProviderDI ContainerSingletonInjectable

NestJS에서 비즈니스 로직을 담당하는 대부분의 클래스는 Provider로 등록된다. Provider는 NestJS의 IoC 컨테이너가 생성과 소멸을 책임지며, 필요한 곳에 자동으로 주입해주는 객체다. @Injectable 데코레이터 하나로 클래스가 DI 대상이 된다.

Provider란 무엇인가

Provider는 @Injectable 데코레이터가 붙은 클래스 또는 값·팩토리로 정의한 객체를 말한다. Module의 providers 배열에 등록하면 NestJS IoC 컨테이너가 인스턴스를 관리한다.

Module
  └─ providers: [CatsService, DogsService]
        │
        ▼
  IoC Container
  ┌────────────────────────────┐
  │  token → instance (cache) │
  │  CatsService → <obj>      │
  │  DogsService → <obj>      │
  └────────────────────────────┘
        │
        ▼ constructor injection
  CatsController

IoC(Inversion of Control): 객체 생성 제어권을 개발자가 아닌 프레임워크가 가져가는 패턴.

Provider 등록 방식

NestJS는 세 가지 Provider 등록 문법을 지원한다.

방식용도예시
Class Provider일반 서비스 클래스useClass: CatsService
Value Provider상수·설정 객체 주입useValue: { apiKey: '...' }
Factory Provider런타임에 동적 생성useFactory: () => new Service

Class Provider — 가장 흔한 방식으로, 단축 문법과 명시적 문법이 동일하게 동작한다.

// 단축 문법
@Module({ providers: [CatsService] })

// 명시적 문법 (동일한 의미)
@Module({
  providers: [{ provide: CatsService, useClass: CatsService }],
})

Value Provider — 외부 설정이나 mock 객체를 토큰으로 바인딩할 때 사용한다.

const mockCatsService = { findAll: () => [] };

@Module({
  providers: [{ provide: CatsService, useValue: mockCatsService }],
})

Factory Provider — 비동기 초기화나 조건부 생성이 필요한 경우에 쓴다.

@Module({
  providers: [
    {
      provide: 'DB_CONNECTION',
      useFactory: async (config: ConfigService) => {
        return createConnection(config.get('DB_URL'));
      },
      inject: [ConfigService],
    },
  ],
})

inject 배열에 나열한 토큰이 useFactory 함수의 인자로 순서대로 전달된다.

@Inject 와 커스텀 토큰

클래스가 아닌 문자열·Symbol 토큰을 주입받을 때는 @Inject 데코레이터를 명시해야 한다.

@Injectable()
export class CatsService {
  constructor(
    @Inject('DB_CONNECTION') private readonly db: Connection,
  ) {}
}

클래스 타입 토큰은 TypeScript 리플렉션으로 자동 해석되므로 @Inject가 불필요하다. 문자열·Symbol 토큰은 런타임에 타입 정보가 사라지기 때문에 반드시 명시해야 한다.

Singleton 스코프

NestJS Provider의 기본 스코프는 Singleton이다. 같은 Module 내에서 여러 클래스가 동일한 Provider를 주입받아도 컨테이너는 인스턴스를 한 번만 생성하고 캐싱해 재사용한다.

Request A ──┐
             ├──▶ CatsService (동일 인스턴스)
Request B ──┘

요청마다 새 인스턴스가 필요하면 @Injectable({ scope: Scope.REQUEST })로 스코프를 변경할 수 있지만, Singleton 대비 성능 비용이 발생한다.

exports로 Provider 공유

다른 Module에서 Provider를 사용하려면 exports에 추가해야 한다.

@Module({
  providers: [CatsService],
  exports: [CatsService],   // 외부 Module에서 주입 가능
})
export class CatsModule {}

exports에 없는 Provider는 Module 경계 밖에서 접근할 수 없다. 이 캡슐화 덕분에 의존성 그래프가 명시적으로 유지된다.

← 이전 글NestJS Service — 비즈니스 로직을 담당하는 클래스
다음 글 →NestJS Dependency Injection — 의존성을 자동으로 주입하는 방식