처음부터 차근차근

[TIL - 231127] NestJS 공식 문서 정리 및 강의 수강 본문

TIL

[TIL - 231127] NestJS 공식 문서 정리 및 강의 수강

HangJu_95 2023. 11. 28. 00:12
728x90

[TIL - 23XXXX]

오늘 한 일

  • NestJS 강의 수강
  • NestJS 공식 문서 기초 정리

NestJS 강의 수강

  • Fastcampus 강의를 들으면서 많은 모듈 사용방법을 간단하게 익혔다.
  • 과거 TypeORM migration 방법도 다시한번 정리하였다.

1. interceptor를 통해 Pagenation을 위한 데이터 로직 변경 진행

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Request } from 'express';
import { Observable, map } from 'rxjs';

// Pagenation을 위한 Interceptor 구현 진행
@Injectable()
export class TransformInterceptor<T, R> implements NestInterceptor<T, R> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<R> {
    return next.handle().pipe(
      // rxjs의 map 메서드를 이용한다.
      map((data) => {
        // http request에서 query를 받아오기 위함.
        const http = context.switchToHttp();
        const request = http.getRequest<Request>();

        // return 하는 객체가 배열일 경우 페이지네이션을 위해 객체 항목을 수정한다.
        // 이는 저번에 작성했던 Swagger에 있는 명세와 동일하게 맞추기 위함.
        if (Array.isArray(data)) {
          return {
            page: Number(request.query['page'] || 1),
            size: Number(request.query['size'] || 20),
            items: data,
          };
        } else {
          return data;
        }
      }),
    );
  }
}

2. swagger에 보안 지정

  // main.ts
  import * as basicAuth from 'express-basic-auth';
  // ..생략
  
  const configService = app.get(ConfigService);
  const stage = configService.get('STAGE');
    // local과 dev 환경에서만 Swagger가 보일수 있게 설정하기
  const SWAGGER_ENVS = ['local', 'dev'];
  if (SWAGGER_ENVS.includes(stage)) {
    app.use(
      // swagger 경로 암호 설정
      ['/docs', 'docs-json'],
      // express-basic-auth를 통해 암호 설정 진행
      basicAuth({
        challenge: true,
        users: {
          [configService.get('swagger.user')]: configService.get('swagger.password'),
        },
      }),
    );
    const config = new DocumentBuilder()
      .setTitle('NestJS project')
      .setDescription('NestJS project API description')
      .setVersion('1.0')
      .addBearerAuth()
      .build();
      
    const customOption: SwaggerCustomOptions = {
      swaggerOptions: {
        persistAuthorization: true,
      },
    };
    const document = SwaggerModule.createDocument(app, config);
    SwaggerModule.setup('docs', app, document, customOption);
  }

3. env에 데이터 입력

import { registerAs } from '@nestjs/config';

// registerAS를 통해 configObject와 Factory를 설정해준다.
export default registerAs('swagger', async () => {
  return {
    user: process.env.SWAGGER_USER || 'park',
    password: process.env.SWAGGER_PASSWORD || 'park',
  };
});

4. Rate Limit 설정 진행

Throttler를 통해 brutal force 방어하며, guard를 통해 적용한다.

import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';
import { Body, Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiExtraModels, ApiTags } from '@nestjs/swagger';
import { VideoService } from './video.service';
import { CreateVideoReqDto, FindVideoReqDto } from './dto/req.dto';
import { PageReqDto } from 'src/common/dto/req.dto';
import { CreateVideoResDto, FindVideoResDto } from './dto/res.dto';
import { ApiGetItemsResponse, ApiGetResponse, ApiPostResponse } from 'src/common/decorator/swagger.decorator';
import { PageResDto } from 'src/common/dto/res.dto';
import { ThrottlerBehindProxyGuard } from 'src/common/guards/throttler-behind-proxy.guard';
import { SkipThrottle, Throttle } from '@nestjs/throttler';

@UseGuards(ThrottlerBehindProxyGuard)
@ApiTags('Video')
@ApiExtraModels(FindVideoReqDto, PageReqDto, CreateVideoResDto, FindVideoResDto, PageResDto)
@Controller('api/videos')
export class VideoController {

  @ApiBearerAuth()
  @ApiGetItemsResponse(FindVideoResDto)
  @SkipThrottle()
  @Get()
  findAll(@Query() { page, size }: PageReqDto) {
    return this.videoService.findAll();
  export class VideoController {
  }

  @ApiBearerAuth()
  // Throttle을 조금 더 타이트하게 주고싶은 경우
  @Throttle({ default: { ttl: 3, limit: 10 } })
  @Get(':id/download')
  async download(@Param() { id }: FindVideoReqDto) {
    return this.videoService.download(id);
import { Injectable } from '@nestjs/common';
import { ThrottlerGuard } from '@nestjs/throttler';

@Injectable()
export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
  protected getTracker(req: Record<string, any>): Promise<string> {
    // IP가 여러 군데를 통과해서 들어오는데, 가장 첫번째 IP가 클라이언트이다.
    return req.ips.length ? req.ips[0] : req.ip;
  }
}
import { Video } from './entity/video.entity';
import { VideoController } from './video.controller';
import { VideoService } from './video.service';
import { APP_GUARD } from '@nestjs/core';
import { ThrottlerGuard } from '@nestjs/throttler';

@Module({
  imports: [TypeOrmModule.forFeature([Video])],
  controllers: [VideoController],
  providers: [VideoService],
  providers: [VideoService, { provide: APP_GUARD, useClass: ThrottlerGuard }],
})
export class VideoModule {}

 

다양한 것들을 공부하였으며, 이것들 또한 정리가 필수적이다.

NestJS 공식문서 정리 진행

  • NestJS 강의를 들으면서, 대부분의 내용이 공식 문서에 해당되는 것을 확인하였다.
  • 따라서 이 부분도 같이 정리를 하면 좋겠다는 생각을 하였다.
  • 하지만 해석하는데 시간이 오래 걸리는 것 같아, 간단하게 정리하는 것이 좋겠다는 판단.