코딩캠프/내일배움캠프

[ TIL ] 02.24(금) 73일차

고랑E 2023. 2. 24. 21:00
728x90

Nest.js 심화

 

7. 캐싱 사용하기

자주 변하지 않는 데이터에 동일한 요청이 지속적으로 들어오는 경우에 대해서는 캐싱 기능을 사용할 수 있다면 서버의 성능이 전반적으로 올라가겠죠?

캐싱은 사용할 수 있으면 무조건 사용하는 것이 성능을 끌어올릴 수 있는 비결

 

Nest.js 에서는 cache-manager 를 사용해서 캐싱 기능을 사용할 수 있다.

 

cache-manager 설치

npm i cache-manager
npm i -D @types/cache-manager

 

모듈에 패키지 적용

app.module.ts

import {
  CacheModule,
  MiddlewareConsumer,
  Module,
  NestModule,
  RequestMethod,
} from "@nestjs/common";




    CacheModule.register({
      ttl: 60000, // 데이터 캐싱 시간(밀리 초 단위, 1000 = 1초)
      max: 100, // 최대 캐싱 개수
      isGlobal: true,

 

서비스에 캐시 매니저 ID 하기

board.service.ts 1차

import {
  CACHE_MANAGER,
  Inject,
  Injectable,
  NotFoundException,
  UnauthorizedException,
} from '@nestjs/common';
import _ from 'lodash';
import { Cache } from 'cache-manager';
import { ArticleRepository } from './article.repository';

@Injectable()
export class BoardService {
  constructor(
    @Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
    private articleRepository: ArticleRepository,
  ) {}

  async getArticles() {
    const cachedArticles = await this.cacheManager.get('articles');
    if (!_.isNil(cachedArticles)) {
      return cachedArticles;
    }

    const articles = await this.articleRepository.find({
      where: { deletedAt: null },
      select: ['author', 'title', 'updatedAt'],
    });
    await this.cacheManager.set('articles', articles);
    return articles;
  }

이 코드는 다음과 같은 순서로 실행됩니다.

  • 캐시를 뒤져본다.
    • 캐시 히트가 되었다면 해당 캐시를 리턴한다.
    • 캐시 미스가 되었다면 다음과 같이 행동한다.
      • 리포지토리를 통해 데이터베이스에 접근해서 결과물을 가져온다.
      • 결과물을 캐싱한다.
      • 결과물을 리턴한다.

여기서는 “articles”라는 키를 통해 this.cacheManager.get 함수를 통해서 캐시를 뒤져보고 캐싱이 되었다면 캐싱된 결과(cachedArticles)를 리턴하고 그렇지 않으면 리포지토리로 결과 값을 가져온 뒤 this.cacheManager.set 함수를 통해서 캐싱을 한 후 결과물을 리턴

 

캐싱을 적용해도 크게 문제가 없는 서비스 로직에 한해서는 적극적으로 캐싱을 적용하는 것을 추천

max 속성을 통해 캐싱이 되는 데이터 양도 조절을 할 수 있어 메모리가 오버플로우 날 걱정은 딱히하지 않아도 된다.

지금은 인-메모리 기반으로 캐싱을 했지만 Redis와 같은 인-메모리 데이터베이스를 사용하는 것도 가능하니 항상 적극적으로 고려하면 좋다!

 

8. Rate limit 적용하기

DDos 공격이나 brute force 공격 등으로 서버가 다운되는 상황이 오게된다

이럴 때 Rate limit 를 사용하여 어느정도 방어가 가능하다.

 

최소한의 방어책을 구축해 놓는게 좋다.

@nestjs/throttler 를 설치

npm i @nestjs/throttler

 

모듈에 @nestjs/throttler 추가

app.module.ts

import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';



ThrottlerModule.forRoot({
      ttl: 60,
      limit: 10, // ttl 동안 limit 만큼의 요청만 받는다.
    }),
    BoardModule,
    UserModule,
  ],
  controllers: [AppController],
  providers: [
    AppService,
    AuthMiddleware,
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],

ThrottlerModule.forRoot 함수를 통해서 Rate limit 옵션을 글로벌하게 적용

limit 이상의 요청을 하게 되면 요청에 응답을 하지 않는다.

 

또한, imports 뿐 아니라 providers에도 추가적으로 코드를 작성해주어 글로벌하게 Rate limit을 적용

 

 

경우에 따라서는 특정 API는 Rate limit 기능 적용을 하고 싶지 않은 경우가 있을때는

 

@SkipThrottle() 데코레이터를 API 위에 명시 해주면 된다.

 

또는 @Throttle(limit, ttl) 데코레이터를 추가하여

Rate limit 정책을 다르게 줄수도 있다.

 

@SkipThrottle() // 데코레이터 추가!
@Get("/articles")
async getArticles() {
  return await this.boardService.getArticles();
}



@Throttle(5, 60) // 이렇게 하면 60초에 5번 호출만 가능!
@Get("/articles/:id")
async getArticleById(@Param("id") articleId: number) {
  return await this.boardService.getArticleById(articleId);
}