Skip to main content

Command Palette

Search for a command to run...

[ 살펴보기 ] NestJS - Providers

Updated
4 min readView as Markdown
[ 살펴보기 ] NestJS - Providers
C

A developer living in Busan, Korea

Service, Repository, Helper등의 Provider로 관심사를 분리하고 필요한 곳에 Provider를 의존성으로 주입하므로서 사용한다. 예를들어 Client로부터 요청을 받아 실제로 데이터를 조회, 생성, 수정하는 로직의 수행은 Controller에서 직접 하지 않고 Service에 위임하여 작업을 처리한다

다음 UserService 코드를 살펴보자

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  create(createUserDto: CreateUserDto) {
    ...
  }

  findAll() {
    ...
  }

  findOne(id: string) {
    ...
  }

  update(id: string, updateUserDto: UpdateUserDto) {
    ...
  }

  remove(id: string) {
    ...
  }
}

위의 코드에서 볼 수 있듯이 Service는 @Injectable decorator를 통해 다른 Controller에 주입할 수 있는 Provider가 된다. 위 처럼 선언한 Service를 사용하고자 하는 module의 provider로 제공하여 사용할 수 있다.

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

UserController에서 받은 요청에 필요한 로직 수행은 Service로 위임해야 하므로 UserService를 UserModule의 provider로 제공한다.

그리고 Controller를 생성자를 통해 service를 주입받아 사용한다

import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(id);
  }
}

만약 order 요청을 관리하는 controller에서 user service를 사용해야 할 때는 어떻게 할까? 이 때도 마찬가지로 order module provider에 user service를 제공해야 한다

import { Module } from '@nestjs/common';
import { OrdersService } from './orders.service';
import { OrdersController } from './orders.controller';
import { UsersService } from 'src/users/users.service';

@Module({
  controllers: [OrdersController],
  providers: [OrdersService, UsersService],
})
export class OrdersModule {}

그러면 order controller 생성자에서 user service를 주입받아 사용할 수 있게 된다

import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';
import { OrdersService } from './orders.service';
import { CreateOrderDto } from './dto/create-order.dto';
import { UpdateOrderDto } from './dto/update-order.dto';
import { UsersService } from 'src/users/users.service';

@Controller('orders')
export class OrdersController {
  constructor(
    private readonly ordersService: OrdersService,
    private readonly usersService: UsersService, // user service
  ) {}

  @Post()
  create(@Body() createOrderDto: CreateOrderDto) {
    return this.ordersService.create(createOrderDto);
  }

  @Get()
  findAll() {
    // 주입받은 user service를 사용
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.ordersService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateOrderDto: UpdateOrderDto) {
    return this.ordersService.update(+id, updateOrderDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.ordersService.remove(+id);
  }
}

Custom Provider

Nest에서 사용 기본적인 Provider외에 Custom Provider가 필요하다면 Provider를 직접 생성하여 사용할 수 있다.

다음 코드를 살펴보자. 우리는 위의 예제에서 provider property에 UserService Class를 바로 전달해주었다.

@Module({
  ...
  providers: [UsersService],
})

하지만 위의 코드를 풀어 헤치면 아래 코드와 같다


providers: [
  {
    provide: UsersService, // provider token
    useClass: UsersService, // provider 
  },
];

첫 번째 예제와 같이 providers array에 Class를 바로 전달하면 NestJS는 자동으로 provider와 provider token 값을 Class name으로 설정한다

만약 우리가 간단한 테스트를 위해 provider token은 유지한채 기존 provider 대신 테스트용 객체를 전달하여 새로운 동작을 테스트 해보고 싶다면 어떻게 할 수 있을까? 다음 예제를 살펴보자.


const testUserService = {
  create(createUserDto: CreateUserDto) {
    ...
  },
  findAll() {
   ...
  },
  ...
};

providers: [
  {
    provide: UsersService,
    useValue: testUserService,
  },
];

위의 예제처럼 testUserService라는 객체를 useValue에 설정해주면 UsersService Provider는 기존 UserService Class 대신 testUserService를 기준으로 실행되며 testUsersive 내부의 정의된 로직을 테스트 해볼 수 있다. ( 위의 예제에선 useValue에 전달하는 값은 provide에 전달하는 class의 interface와 호환이 되는 객체이거나 new 키워드로 생성된 인스턴스 여야 한다 )

물론 provider token에 반드시 class를 설정해야 하는 것은 아니다. 다음과 같이 임의 텍스트로 token을 설정하여 사용할 수 있다

const testUserService = {
  create(createUserDto: CreateUserDto) {
    ...
  },
  findAll() {
   ...
  },
  ...
};

providers: [
  {
    provide: 'TestUserService',
    useValue: testUserService,
  },
];

만약 위 처럼 provider token을 임의의 값으로 변경했다면 provider를 주입하는 곳에서도 @Inject decorator를 통해 명시적으로 provider token을 기입해준다

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

@Controller('users')
export class UsersController {
  constructor(
    @Inject('TestUserService') private readonly usersService: UsersService,
  ) {}

  ...

}

useFactory property를 통해 provider를 동적으로 생성할 수도 있다. 다음의 예를 살펴보자

@Module({
  ...
  providers: [
    {
      provide: 'UserServiceFactory',
      useFactory:()=>{
        return new UserServiceFactory()
      }
    },
  ],
})

위의 예제에서 UserServiceFactory provider는 useFactor 함수가 return하는 instance가 된다. 만약에 useFactory 함수에 다른 provider를 주입해야 한다면 다음과 같이 inject 배열에 해당 provider를 주입해준다. inject에 주입하는 provider가 현재 module에 주입 되어야 있어야 하는 것에 주의하자

@Module({
  controllers: [UsersController],
  providers: [
    OrdersService,
    {
      provide: 'TestOrderFactory',
      useFactory: (ordersService: OrdersService) => {
        ...
        return new TestOrderFactory();
      },
      inject: [OrdersService],
    },
  ],
  exports: [UsersService],
})

위의 예제에선 OrderService를 useFactory의 provider로 주입하여 사용하고 있다. 그리고 useFactory의 provider로 주입하기 위해선 해당 module에도 OrderService가 주입되어 있어야 한다.

More from this blog

[ 살펴보기 ] TypeORM - Transactions, Migration

Transation Database 종류에 따라 detail한 부분은 차이점이 조금씩 있겠지만 각 sql statement는 개별적인 transaction block을 통해 실행되며 Database 설정에 따라 sql statement의 실행 결과가 자동으로 commit되어 영구히 적용되거나 commit을 직접 실행하기 전까지는 영구히 적용되지 않을 수 있다. 대부분의 경우 default로 sql statement 실행 결과가 자동으로 comm...

Feb 9, 20256 min read
[ 살펴보기 ] TypeORM - Transactions, Migration

Dev Diary

184 posts

[ 살펴보기 ] NestJS - Providers