icon

미들웨어, 인터셉터, 파이프, 가드


 NestJS는 애플리케이션의 요청-응답 주기를 세밀하게 제어할 수 있는 다양한 컴포넌트를 제공합니다.

 이 절에서는 미들웨어, 인터셉터, 파이프, 가드에 대해 상세히 다룹니다.

미들웨어 (Middleware)

 미들웨어는 요청이 라우트 핸들러에 도달하기 전에 실행되는 함수입니다.

 함수형 미들웨어

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`Request...`);
  next();
}

 클래스 미들웨어

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

 클래스 미들웨어는 의존성 주입을 활용할 수 있어 더 복잡한 로직 구현에 적합합니다.

인터셉터 (Interceptors)

 인터셉터는 요청과 응답을 가로채고 변형할 수 있는 강력한 도구입니다.

@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`)),
      );
  }
}

 인터셉터는 다음과 같은 용도로 사용될 수 있습니다.

  • 메소드 실행 전후에 추가 로직 바인딩
  • 함수에서 반환된 결과 변환
  • 예외 변환
  • 기본 기능 확장
  • 특정 조건에 따라 함수 완전히 재정의

파이프 (Pipes)

 파이프는 주로 데이터 변환과 유효성 검사에 사용됩니다.

 내장 파이프

@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}

 커스텀 파이프

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    // 유효성 검사 로직
    return value;
  }
}

가드 (Guards)

 가드는 주로 인증과 인가에 사용됩니다.

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return validateRequest(request);
  }
}

 가드는 @UseGuards() 데코레이터를 통해 컨트롤러나 메소드에 적용할 수 있습니다.

실행 순서와 데이터 흐름

 NestJS에서 요청 처리 순서는 다음과 같습니다.

  1. 미들웨어
  2. 가드
  3. 인터셉터 (pre-controller)
  4. 파이프
  5. 컨트롤러
  6. 서비스
  7. 인터셉터 (post-request)
NestJS 흐름

NestJS 흐름

전역 및 특정 적용

 전역 적용

// main.ts
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
app.useGlobalGuards(new AuthGuard());
app.useGlobalInterceptors(new LoggingInterceptor());

 특정 적용

@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {
  @Post()
  @UseInterceptors(LoggingInterceptor)
  @UsePipes(ValidationPipe)
  create(@Body() createCatDto: CreateCatDto) {
    // ...
  }
}

복잡한 비즈니스 로직 구현

 여러 컴포넌트를 조합하여 복잡한 비즈니스 로직을 구현할 수 있습니다.

@Controller('users')
@UseGuards(AuthGuard)
export class UsersController {
  @Post()
  @UseInterceptors(LoggingInterceptor, CachingInterceptor)
  @UsePipes(ValidationPipe)
  async create(@Body() createUserDto: CreateUserDto) {
    // 사용자 생성 로직
  }
}

 이 예시에서는 인증, 로깅, 캐싱, 유효성 검사를 모두 조합하여 사용자 생성 로직을 구현했습니다.

Best Practices 및 성능 고려사항

 1. 미들웨어

  • 간단한 로직은 함수형 미들웨어를 사용하여 성능 최적화
  • 복잡한 로직이나 의존성 주입이 필요한 경우 클래스 미들웨어 사용

 2. 인터셉터

  • RxJS 연산자를 활용하여 강력한 스트림 조작 구현
  • 성능에 민감한 작업은 가능한 한 인터셉터 외부로 이동

 3. 파이프

  • 가능한 한 내장 파이프 활용
  • 커스텀 파이프는 재사용 가능하도록 설계

 4. 가드

  • 인증/인가 로직은 가능한 한 가드로 구현하여 관심사 분리
  • 성능을 위해 캐싱 전략 고려

 5. 전반적인 고려사항

  • 각 컴포넌트의 책임을 명확히 분리
  • 불필요한 중복 로직 제거
  • 비동기 작업은 가능한 한 병렬 처리
  • 성능 병목 지점 식별을 위해 주기적으로 프로파일링 수행

 NestJS의 미들웨어, 인터셉터, 파이프, 가드는 애플리케이션의 요청-응답 주기를 세밀하게 제어할 수 있게 해주는 도구입니다.

 미들웨어는 주로 애플리케이션 레벨에서의 전처리 작업에 사용되며, 인터셉터는 더 세밀한 제어와 후처리 작업에 적합합니다.

 파이프는 데이터 변환과 유효성 검사에 특화되어 있으며, 가드는 인증과 인가 로직을 깔끔하게 분리할 수 있게 해줍니다.