포시코딩

2월24일 - @nestjs/throttler 본문

TIL

2월24일 - @nestjs/throttler

포시 2023. 2. 24. 00:44
728x90

개요

Nest.js에서는 @nestjs/throttler 를 통해 

특정 유저(IP 기준)가 짧은 시간내에 여러 번 같은 API를 호출하는 것을 방지할 수 있다.

이것을 Rate Limit 기능이라고 한다.

 

설치

https://www.npmjs.com/package/@nestjs/throttler

 

@nestjs/throttler

A Rate-Limiting module for NestJS to work on Express, Fastify, Websockets, Socket.IO, and GraphQL, all rolled up into a simple package.. Latest version: 4.0.0, last published: a month ago. Start using @nestjs/throttler in your project by running `npm i @ne

www.npmjs.com

npm i @nestjs/throttler

 

사용 방법

코드

app.module.ts

// ...다른 import 생략
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler'
import { APP_GUARD } from '@nestjs/core';

@Module({
  imports: [
    // ...생략
    ThrottlerModule.forRoot({
      ttl: 60,
      limit: 2,  // ttl 동안 limit 번 만큼의 요청만 받는다.
    }),
  ],
  controllers: [AppController],
  providers: [
    AppService, 
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    }
  ],
})
export class AppModule {}

app.module에서 간단하게 설정할 수 있는데

 

 

ThrottlerModule.forRoot({
  ttl: 60,
  limit: 2,  // ttl 동안 limit 번 만큼의 요청만 받는다.
}),

import에 위 코드를 추가하고

 

{
  provide: APP_GUARD,
  useClass: ThrottlerGuard,
}

providers에 위 코드를 추가하면 된다.

 

이상하게 나는 @nestjs/throttler 관련 class들이 자동완성으로 import 되지 않는 문제가 있어

직접 import를 다 써주었다.

 

이렇게 작성하면 위의 ttl, limit에 적은 옵션을 통해

모든 API에 대해 60초 동안 2번 만큼의 요청만 할 수 있다.

 

결과 확인

3번 이상 호출 시 429 status code 에러를 내며 

ThrottleException을 통해 너무 많은 요청이라는 메시지를 받게 된다.

 

https://developer.mozilla.org/ko/docs/Web/HTTP/Status/429

429 status code 추가 설명.

 

추가 기능

하지만 이렇게 모든 API에 대해 제한을 걸어 버리면

throttler를 적용하고 싶지 않은 API에도 제한이 걸리게 된다.

 

그럴 때는 아래와 같이 사용할 데코레이터들이 존재한다.

 

@SkipThrottle()

@SkipThrottle()
@Get('/hot-articles')
async getHotArticles() {
  return await this.boardService.getHotArticles();
}

이처럼 controller의 메서드 위에 사용 시 해당 API에 대해서는 throttler의 제한 없이 

사용이 가능하며

 

@SkipThrottle()
@Controller()
export class AppController {

  @SkipThrottle(false)
  dontSkip() {}  // app.module.ts에서 설정한 글로벌 throttle 설정이 적용된다.

  doSkip() {}  // controller 위의 @SkipThrottle() 덕분에 throttle 제한과 관계없이 사용가능
}

이런식으로 controller 전체에 @SkipThrottle()을 먹인 후 

특정 메서드에만 @SkipThrottle(false)를 적용할 수도 있다.

 

@Throttle()

@Throttle(5, 60)
@Get('/hot-articles')
async getHotArticles() {
  return await this.boardService.getHotArticles();
}

아니면 직접 Rate limit을 특별하게 적용할 수도 있다.

@Throttle(limit, ttl)의 형태로 적용하면 위의 메서드는 60초에 5번까지만 호출이 가능하게 변한다.

 

++ 참고사항

나는 이상하게 @SkipThrottle()과 @Throttle()도 import 자동완성이 되지 않아 헤맸었는데

import { SkipThrottle, Throttle } from '@nestjs/throttler';

직접 작성하고 나서야 정상 작동을 확인할 수 있었다. 참고!

 

정리

자의든 타의든 특정 API에 대해 한번에 여러 번 요청하는 일이 생길 수 있고

만에 하나 DDoS, DoS 같은 공격이 있을 때

DDoS 공격 방어는 힘들더라도 DoS 공격에 대한 방어는 어느 정도 가능하니 

 

Nest.js로 서버를 구축한다면 @nestjs/throttler를 통해 최소한의 방어책을 구축하길 추천한다.

728x90