NestJS의 미들웨어, 파이프, 가드 - maeng2418/zzik-meok GitHub Wiki

📌 요청 처리 순서

NestJS에서 HTTP 요청 처리 순서:

  1. 미들웨어 (Middleware)
  2. 가드 (Guard)
  3. 인터셉터 (Interceptor) - 요청 전
  4. 파이프 (Pipe)
  5. 컨트롤러 메서드 실행
  6. 인터셉터 (Interceptor) - 응답 후
  7. 예외 필터 (Exception Filter) - 예외 발생 시

🔄 미들웨어 (Middleware)

개요

미들웨어는 라우트 핸들러가 실행되기 전에 호출되는 함수로, 요청 및 응답 객체에 접근하여 수정할 수 있습니다.

주요 특징

  • 실행 시점: 요청 처리 사이클에서 가장 먼저 실행됨
  • 기능: 요청/응답 객체 수정, 요청-응답 사이클 종료, 다음 미들웨어로 제어 전달
  • 사용 사례: 로깅, CORS 처리, 요청 파싱, 헤더 수정, 압축/해제
  • 적용 범위: 전역 또는 특정 경로 패턴에 적용 가능
  • 구현 방법: 함수 또는 NestMiddleware 인터페이스 구현 클래스

예시 코드

// 함수형 미들웨어
export function logger(req: Request, res: Response, next: Function) {
  console.log(`Request... ${req.method} ${req.path}`)
  next()
}

// 클래스형 미들웨어
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    console.log(`Request... ${req.method} ${req.path}`)
    next()
  }
}

// 적용 예시 (AppModule)
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('users')
  }
}

🛡️ 가드 (Guard)

개요

가드는 특정 라우트에 대한 접근 권한을 결정하는 역할을 합니다. 주로 인증 및 권한 부여에 사용됩니다.

주요 특징

  • 실행 시점: 미들웨어 이후, 파이프 이전에 실행됨
  • 기능: 특정 요청의 처리 여부를 결정(true/false 반환)
  • 사용 사례: 인증, 권한 부여, 역할 기반 접근 제어(RBAC)
  • 적용 범위: 전역, 컨트롤러, 메서드 레벨에 적용 가능
  • 구현 방법: CanActivate 인터페이스 구현 클래스

예시 코드

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  canActivate(context: ExecutionContext): boolean | Promise<boolean> {
    const request = context.switchToHttp().getRequest()
    try {
      const token = request.headers.authorization.split(' ')[1]
      const payload = this.jwtService.verify(token)
      request.user = payload
      return true
    } catch (e) {
      return false
    }
  }
}

// 적용 예시
@UseGuards(AuthGuard)
@Controller('users')
export class UsersController {
  // ...
}

🧪 파이프 (Pipe)

개요

파이프는 클라이언트 요청에서 들어온 데이터를 변환하거나 검증하는 역할을 합니다.

주요 특징

  • 실행 시점: 라우트 핸들러가 실행되기 직전, 가드 이후 실행됨
  • 기능: 입력 데이터 변환 및 유효성 검사
  • 사용 사례: DTO 유효성 검사, 파라미터 타입 변환, 데이터 정제
  • 적용 범위: 전역, 컨트롤러, 메서드, 파라미터 레벨에 적용 가능
  • 구현 방법: PipeTransform 인터페이스 구현 클래스

예시 코드

// 내장 ValidationPipe 사용
@Post()
createUser(@Body(new ValidationPipe()) createUserDto: CreateUserDto) {
  return this.usersService.create(createUserDto);
}

// 커스텀 파이프 예시
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
  transform(value: string, metadata: ArgumentMetadata): number {
    const val = parseInt(value, 10);
    if (isNaN(val)) {
      throw new BadRequestException('Validation failed');
    }
    return val;
  }
}

// 적용 예시
@Get(':id')
findOne(@Param('id', new ParseIntPipe()) id: number) {
  return this.usersService.findOne(id);
}

📊 비교표

특성 미들웨어 가드 파이프
주요 목적 요청 전처리, 로깅 인증, 인가 데이터 변환, 유효성 검사
실행 순서 1️⃣ 첫 번째 2️⃣ 두 번째 3️⃣ 세 번째
요청 차단 가능성 가능 (next() 호출 안 함) 가능 (false 반환) 예외 발생으로 간접 차단
접근 가능한 컨텍스트 req, res 객체 ExecutionContext 파라미터 값
적용 범위 전역, 경로 패턴 전역, 컨트롤러, 메서드 전역, 컨트롤러, 메서드, 파라미터

📝 사용 시 고려사항

  1. 미들웨어

    • Express 미들웨어와 호환되어 생태계의 다양한 미들웨어 활용 가능
    • 라우트 특정 로직보다는 애플리케이션 전반적인 기능에 적합
    • 경로 패턴별로 적용 가능하지만 메서드 단위 적용은 불가능
  2. 가드

    • 권한 부여 로직을 컨트롤러에서 분리하여 코드 재사용성 향상
    • ExecutionContext를 통해 실행 컨텍스트에 대한 접근 가능
    • 메타데이터를 활용한 세밀한 권한 제어 가능 (ex: @Roles() 데코레이터)
  3. 파이프

    • 클래스 유효성 검사를 위해 class-validator와 함께 사용하면 효과적
    • 파라미터 레벨까지 적용 가능하여 세밀한 데이터 검증 가능
    • 전역 파이프를 설정하여 모든 엔드포인트의 입력 데이터 검증 가능

🔗 참고 자료

⚠️ **GitHub.com Fallback** ⚠️