Nest.js Request lifecycle

나주엽·2024년 6월 22일
0

nest

목록 보기
2/3

Nest.js 로 개발하며, Guards 와 Pipes 에 대한 지식이 부족한 것 같아 각각의 component 들의 사용 목적과 실행 순서를 파악하기 위해 공부하며 작성한 글입니다. 오류가 있다면 알려주세요.

미리보는 결론
가드가 우선적으로 실행되며, 사용자 인증 및 권한을 확인하는데 사용됩니다.
파이프는 가드가 실행된 이후 실행되며, 값 변환 및 검증에 사용됩니다.

전체 동작 흐름
요청 → 미들웨어 → 가드 → 인터셉터 → 파이프 → 인터셉터 → 예외 필터 → 응답

미들웨어 Middleware

들어온 요청을 가장 처음 만나게 되는 것이 바로 미들웨어이다.

미들웨어는

  • 요청 객체나 응답 객체를 수정
  • 요청-응답 사이클의 종료
  • 다음 미들웨어 호출

등의 역할을 수행할 수 있다.

Nest.js 공식문서에서의 logger.middleware.ts 와 같이 로그 역할도 수행할 수 있는 것 같습니다.

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}

미들웨어에서 요청-응답 사이클의 종료가 수행되지 않으면, 다음 component 로 제어권이 넘어간다.

미들웨어 동작 순서

  1. 첫 번째로, 전역 미들웨어(Global Middleware)가 실행된다.
    여기서 전역 미들웨어는 app.use() 를 사용해 설정된 미들웨어를 의미한다.
  2. 그 다음으로, 모듈에 설정된 미들웨가 실행된다.
    모듈에는 아래와 같이 미들웨어를 적용한다.
    ```tsx
    import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
    import { LoggerMiddleware } from './common/middleware/logger.middleware';
    import { CatsModule } from './cats/cats.module';
    
    @Module({
      imports: [CatsModule],
    })
    export class AppModule implements NestModule {
      configure(consumer: MiddlewareConsumer) {
        consumer
          .apply(LoggerMiddleware)
          .forRoutes('cats');
      }
    }
    ```

가드 Guards

미들웨어를 지나온 요청은 가드를 만나 해당 요청이 Route Handler 에 처리될 지 말지를 결정된다.

주로, 사용자 인증 및 권환 확인에 사용된다.

가드 동작 순서

  1. 전역적으로 설정된 가드가 먼저 실행되고,

    app.useGlobalGuards(new GlobalGuard())
  2. 그 다음으로 컨트롤러에 설정된 가드,

  3. 마지막으로 Route 에 설정된 가드가 실행된다.

    @UseGuards(ControllerGuard)
    @Controller('cats')
    export class CatsController {
      @UseGuards(RouteGuard)
      @Get()
      getCats(): Cats[] {
        return this.catsService.getCats();
      }
    }

인터셉터 Interceptors

인터셉터는 그 다음으로 수행되어 아래와 같은 역할을 할 수 있다.

  • 메소드 실행 전/후 추가 로직 바인딩
  • 함수에서 반환된 결과 혹은 예외 변환
  • 기본 기능 동작 확장
  • 특정 조건에 기능 오버라이딩

인터셉터 동작 순서

요청시에는 가드와 비슷하게 전역 → 컨트롤러 → 라우터 순으로 동작하지만,

응답에서는 요청과 반대로 라우터 → 컨트롤러 → 전역 순으로 동작한다.

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');

    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`)),
      );
  }
}
Before...
After... 1ms

위와 같은 인터셉터를 설정하면, 아래와 같은 결과를 얻게 된다.


파이프 Pipes

위 인터셉터로 부터 제어권을 받게되면 파이프에서 아래와 같은 역할을 수행한다.

  • 변환 Transformation
  • 검증 Validation

파이프 동작 순서

가드와 인터셉터(요청) 과 마찬가지로 전역 → 컨트롤러 → 라우터 순으로 동작한다.


예외 필터 Exception filters

Nest.js 에서 어플리케이션 전 과정에서 처리되지 않은 예외를 처리하는 exception layer 가 내장되어 있다.

예외 필터 동작 순서

예외 필터는 라우터 → 컨트롤러 → 전역 순으로 동작한다.

@UseFilters(new HttpExceptionFilter()) // 2. 컨트롤러
export class CatsController {
	@Post()
	@UseFilters(new HttpExceptionFilter()) // 1. 라우터
	async create(@Body() createCatDto: CreateCatDto) {
	  throw new ForbiddenException();
	}
}
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter()); // 3. 전역
  await app.listen(3000);
}
bootstrap();

정리

위 순서를 그림으로 그려보았다.


참고

0개의 댓글