코딩일상

[nest.js] NestJS 제공 라이브러리 및 데코레이터 완벽 정리 본문

개발 공부/nest.js

[nest.js] NestJS 제공 라이브러리 및 데코레이터 완벽 정리

solutionMan 2025. 12. 14. 14:34
반응형

1. @nestjs/common - 핵심 라이브러리

1.1 주요 카테고리별 분류표

카테고리 항목 용도
Decorators - Class @Module, @Controller, @Injectable, @Catch 클래스 정의
Decorators - Method @Get, @Post, @Put, @Delete, @Patch, @Options, @Head, @All HTTP 메서드
Decorators - Parameter @Body, @Query, @Param, @Headers, @Req, @Res, @Next, @Session, @Ip, @HostParam 요청 데이터 추출
Decorators - Metadata @SetMetadata, @UseGuards, @UseInterceptors, @UsePipes, @UseFilters 메타데이터/미들웨어 적용
Interfaces NestMiddleware, CanActivate, NestInterceptor, PipeTransform, ExceptionFilter 구현 인터페이스
Classes HttpException, BadRequestException, UnauthorizedException, NotFoundException 예외 클래스
Utilities Reflector, ModuleRef, HttpService 유틸리티
Enums HttpStatus, RequestMethod 상수

 

 

2. 전체 구성 요소 도표

@nestjs/common
├─ Decorators (데코레이터)
│  ├─ Class Decorators (클래스 레벨)
│  │  ├─ @Module()                    // 모듈 정의
│  │  ├─ @Controller()                // 컨트롤러 정의
│  │  ├─ @Injectable()                // 프로바이더 정의 (DI 가능)
│  │  ├─ @Catch()                     // 예외 필터 정의
│  │  └─ @Global()                    // 전역 모듈 정의
│  │
│  ├─ Method Decorators (메서드 레벨)
│  │  ├─ HTTP Methods
│  │  │  ├─ @Get()                    // GET 요청
│  │  │  ├─ @Post()                   // POST 요청
│  │  │  ├─ @Put()                    // PUT 요청
│  │  │  ├─ @Delete()                 // DELETE 요청
│  │  │  ├─ @Patch()                  // PATCH 요청
│  │  │  ├─ @Options()                // OPTIONS 요청
│  │  │  ├─ @Head()                   // HEAD 요청
│  │  │  └─ @All()                    // 모든 HTTP 메서드
│  │  │
│  │  ├─ Middleware Application
│  │  │  ├─ @UseGuards()              // Guard 적용
│  │  │  ├─ @UseInterceptors()        // Interceptor 적용
│  │  │  ├─ @UsePipes()               // Pipe 적용
│  │  │  └─ @UseFilters()             // Exception Filter 적용
│  │  │
│  │  ├─ Metadata
│  │  │  ├─ @SetMetadata()            // 커스텀 메타데이터
│  │  │  ├─ @HttpCode()               // HTTP 상태 코드 설정
│  │  │  ├─ @Header()                 // 응답 헤더 설정
│  │  │  ├─ @Redirect()               // 리다이렉트
│  │  │  └─ @Render()                 // 뷰 템플릿 렌더링
│  │  │
│  │  └─ Binding
│  │     ├─ @Bind()                   // 파라미터 바인딩
│  │     └─ @Dependencies()           // 의존성 명시
│  │
│  └─ Parameter Decorators (파라미터 레벨)
│     ├─ Request Object
│     │  ├─ @Req()                    // Express Request 전체
│     │  ├─ @Request()                // @Req()의 별칭
│     │  ├─ @Res()                    // Express Response 전체
│     │  ├─ @Response()               // @Res()의 별칭
│     │  └─ @Next()                   // Express Next 함수
│     │
│     ├─ Request Parts
│     │  ├─ @Body()                   // Request Body
│     │  ├─ @Query()                  // Query String
│     │  ├─ @Param()                  // URL Parameters
│     │  ├─ @Headers()                // Request Headers
│     │  ├─ @Session()                // Session 객체
│     │  ├─ @Ip()                     // Client IP
│     │  └─ @HostParam()              // Sub-domain 파라미터
│     │
│     └─ Custom
│        └─ @createParamDecorator()   // 커스텀 파라미터 데코레이터
│
├─ Interfaces (인터페이스)
│  ├─ Lifecycle
│  │  ├─ NestMiddleware               // Middleware 인터페이스
│  │  ├─ CanActivate                  // Guard 인터페이스
│  │  ├─ NestInterceptor              // Interceptor 인터페이스
│  │  ├─ PipeTransform                // Pipe 인터페이스
│  │  └─ ExceptionFilter              // Exception Filter 인터페이스
│  │
│  ├─ Module
│  │  ├─ DynamicModule                // 동적 모듈
│  │  ├─ ModuleMetadata              // 모듈 메타데이터
│  │  └─ NestModule                   // 모듈 설정
│  │
│  └─ Context
│     ├─ ExecutionContext             // 실행 컨텍스트
│     ├─ ArgumentsHost                // 인자 호스트
│     ├─ HttpArgumentsHost            // HTTP 컨텍스트
│     ├─ RpcArgumentsHost             // RPC 컨텍스트
│     └─ WsArgumentsHost              // WebSocket 컨텍스트
│
├─ Classes (클래스)
│  ├─ Exceptions (예외)
│  │  ├─ HttpException                // 기본 HTTP 예외
│  │  ├─ BadRequestException          // 400
│  │  ├─ UnauthorizedException        // 401
│  │  ├─ NotFoundException            // 404
│  │  ├─ ForbiddenException           // 403
│  │  ├─ NotAcceptableException       // 406
│  │  ├─ RequestTimeoutException      // 408
│  │  ├─ ConflictException            // 409
│  │  ├─ GoneException                // 410
│  │  ├─ PayloadTooLargeException     // 413
│  │  ├─ UnsupportedMediaTypeException// 415
│  │  ├─ UnprocessableEntityException // 422
│  │  ├─ InternalServerErrorException // 500
│  │  ├─ NotImplementedException      // 501
│  │  ├─ BadGatewayException          // 502
│  │  ├─ ServiceUnavailableException  // 503
│  │  └─ GatewayTimeoutException      // 504
│  │
│  ├─ Pipes (내장 Pipe)
│  │  ├─ ValidationPipe               // DTO 검증
│  │  ├─ ParseIntPipe                 // 정수 변환
│  │  ├─ ParseFloatPipe               // 실수 변환
│  │  ├─ ParseBoolPipe                // 불린 변환
│  │  ├─ ParseArrayPipe               // 배열 파싱
│  │  ├─ ParseUUIDPipe                // UUID 검증
│  │  ├─ ParseEnumPipe                // Enum 검증
│  │  ├─ DefaultValuePipe             // 기본값 설정
│  │  └─ ParseFilePipe                // 파일 검증
│  │
│  └─ Others
│     ├─ StreamableFile              // 파일 스트리밍
│     └─ Logger                       // 로거
│
├─ Utilities (유틸리티)
│  ├─ Reflector                       // 메타데이터 읽기
│  ├─ ModuleRef                       // 모듈 참조
│  └─ ContextIdFactory               // 컨텍스트 ID 생성
│
├─ Enums (열거형)
│  ├─ HttpStatus                      // HTTP 상태 코드
│  ├─ RequestMethod                   // HTTP 메서드
│  └─ Scope                           // Provider 스코프
│
└─ Types (타입)
   ├─ Type<T>                         // 클래스 타입
   ├─ Abstract<T>                     // 추상 클래스
   ├─ ArgumentMetadata                // 파라미터 메타데이터
   └─ CallHandler                     // Interceptor 핸들러

 

 

3.1 Class Decorators (클래스 데코레이터)

// ============================================
// @Module() - 모듈 정의
// ============================================
@Module({
  imports: [DatabaseModule],        // 다른 모듈 가져오기
  controllers: [UsersController],   // 컨트롤러 등록
  providers: [UsersService],        // 프로바이더 등록
  exports: [UsersService],          // 다른 모듈에 제공
})
export class UsersModule {}

// ============================================
// @Controller() - 컨트롤러 정의
// ============================================
@Controller('users')               // 라우트 prefix
export class UsersController {}

@Controller({ host: 'admin.example.com' })  // 서브도메인 라우팅
export class AdminController {}

// ============================================
// @Injectable() - 프로바이더 정의 (DI 가능)
// ============================================
@Injectable()
export class UsersService {}

@Injectable({ scope: Scope.REQUEST })  // 요청마다 새 인스턴스
export class RequestScopedService {}

// ============================================
// @Catch() - 예외 필터 정의
// ============================================
@Catch(HttpException)              // 특정 예외만
export class HttpExceptionFilter implements ExceptionFilter {}

@Catch()                           // 모든 예외
export class AllExceptionsFilter implements ExceptionFilter {}

// ============================================
// @Global() - 전역 모듈
// ============================================
@Global()
@Module({
  providers: [ConfigService],
  exports: [ConfigService],
})
export class ConfigModule {}

 

3.2 Method Decorators - HTTP Methods

// ============================================
// HTTP 메서드 데코레이터
// ============================================
@Controller('users')
export class UsersController {
  
  @Get()                           // GET /users
  findAll() {}
  
  @Get(':id')                      // GET /users/:id
  findOne(@Param('id') id: string) {}
  
  @Post()                          // POST /users
  create(@Body() dto: CreateUserDto) {}
  
  @Put(':id')                      // PUT /users/:id
  update(@Param('id') id: string, @Body() dto: UpdateUserDto) {}
  
  @Patch(':id')                    // PATCH /users/:id
  partialUpdate(@Param('id') id: string, @Body() dto: any) {}
  
  @Delete(':id')                   // DELETE /users/:id
  remove(@Param('id') id: string) {}
  
  @Options()                       // OPTIONS /users
  options() {}
  
  @Head()                          // HEAD /users
  head() {}
  
  @All()                           // 모든 HTTP 메서드
  handleAll() {}
}

 

3.3 Method Decorators - Middleware & Metadata

// ============================================
// Middleware Application
// ============================================
@Controller('users')
@UseGuards(AuthGuard)                    // 컨트롤러 레벨
export class UsersController {
  
  @Get()
  @UseGuards(RolesGuard)                 // 메서드 레벨
  @UseInterceptors(LoggingInterceptor)   // Interceptor
  @UsePipes(ValidationPipe)              // Pipe
  @UseFilters(HttpExceptionFilter)       // Exception Filter
  findAll() {}
}

// ============================================
// Metadata
// ============================================
@Get()
@SetMetadata('roles', ['admin'])         // 커스텀 메타데이터
@HttpCode(201)                           // HTTP 상태 코드
@Header('Cache-Control', 'none')         // 응답 헤더
@Redirect('https://nestjs.com', 301)     // 리다이렉트
@Render('users')                         // 뷰 템플릿
findAll() {}

 

3.4 Parameter Decorators (파라미터 데코레이터)

// ============================================
// Request Object 전체
// ============================================
@Get()
findAll(
  @Req() request: Request,               // Express Request 전체
  @Res() response: Response,             // Express Response 전체
  @Next() next: NextFunction,            // Express Next 함수
) {}

// ============================================
// Request Parts (요청 부분 추출)
// ============================================
@Post()
create(
  @Body() createDto: CreateUserDto,      // 전체 Body
  @Body('name') name: string,            // Body의 특정 필드
  
  @Query() query: any,                   // 전체 Query String
  @Query('page') page: number,           // 특정 Query 파라미터
  
  @Param() params: any,                  // 전체 URL 파라미터
  @Param('id') id: string,               // 특정 URL 파라미터
  
  @Headers() headers: any,               // 전체 헤더
  @Headers('authorization') auth: string,// 특정 헤더
  
  @Session() session: any,               // Session 객체
  @Ip() ip: string,                      // Client IP
  @HostParam('account') account: string, // Sub-domain 파라미터
) {}

// ============================================
// 실전 예시
// ============================================
@Get(':vehicleId/history')
getChargingHistory(
  @Param('vehicleId') vehicleId: string,           // URL에서
  @Query('startDate') startDate: string,           // Query String에서
  @Query('limit', ParseIntPipe) limit: number,     // Query + Pipe
  @Headers('authorization') token: string,         // Header에서
  @Req() request: Request,                         // Request 전체
) {
  console.log(request.user);  // Middleware에서 추가한 데이터
  return this.service.getHistory(vehicleId, { startDate, limit });
}
 

 

3.5 Interfaces (구현해야 할 인터페이스들)

// ============================================
// NestMiddleware - Middleware 인터페이스
// ============================================
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}

// ============================================
// CanActivate - Guard 인터페이스
// ============================================
@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean | Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    return !!request.user;
  }
}

// ============================================
// NestInterceptor - Interceptor 인터페이스
// ============================================
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');
    return next.handle().pipe(
      tap(() => console.log('After...'))
    );
  }
}

// ============================================
// PipeTransform - Pipe 인터페이스
// ============================================
@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;
  }
}

// ============================================
// ExceptionFilter - Exception Filter 인터페이스
// ============================================
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();
    
    response.status(status).json({
      statusCode: status,
      message: exception.message,
    });
  }
}

 

3.6 Exception Classes (예외 클래스들)

// ============================================
// 4xx Client Errors
// ============================================
throw new BadRequestException('Invalid input');              // 400
throw new UnauthorizedException('Invalid credentials');      // 401
throw new ForbiddenException('Access denied');               // 403
throw new NotFoundException('User not found');               // 404
throw new NotAcceptableException('Not acceptable');          // 406
throw new RequestTimeoutException('Request timeout');        // 408
throw new ConflictException('Email already exists');         // 409
throw new GoneException('Resource gone');                    // 410
throw new PayloadTooLargeException('File too large');        // 413
throw new UnsupportedMediaTypeException('Unsupported');      // 415
throw new UnprocessableEntityException('Invalid data');      // 422

// ============================================
// 5xx Server Errors
// ============================================
throw new InternalServerErrorException('Server error');      // 500
throw new NotImplementedException('Not implemented');        // 501
throw new BadGatewayException('Bad gateway');                // 502
throw new ServiceUnavailableException('Service down');       // 503
throw new GatewayTimeoutException('Gateway timeout');        // 504

// ============================================
// 커스텀 메시지 및 옵션
// ============================================
throw new BadRequestException({
  statusCode: 400,
  message: 'Validation failed',
  error: 'Bad Request',
  details: ['name is required', 'email is invalid'],
});

// ============================================
// 기본 HttpException 사용
// ============================================
throw new HttpException('Custom error', HttpStatus.I_AM_A_TEAPOT);
throw new HttpException({
  status: HttpStatus.FORBIDDEN,
  error: 'This is a custom message',
}, HttpStatus.FORBIDDEN);

 

3.7 Built-in Pipes (내장 파이프들)

// ============================================
// ValidationPipe - DTO 검증
// ============================================
@Post()
create(@Body(new ValidationPipe()) dto: CreateUserDto) {}

// 글로벌 적용
app.useGlobalPipes(new ValidationPipe({
  whitelist: true,              // DTO에 없는 속성 제거
  forbidNonWhitelisted: true,   // DTO에 없는 속성 있으면 에러
  transform: true,              // 자동 타입 변환
}));

// ============================================
// ParseIntPipe - 문자열 → 정수
// ============================================
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {}

// ============================================
// ParseFloatPipe - 문자열 → 실수
// ============================================
@Get()
findAll(@Query('price', ParseFloatPipe) price: number) {}

// ============================================
// ParseBoolPipe - 문자열 → 불린
// ============================================
@Get()
findAll(@Query('active', ParseBoolPipe) active: boolean) {}

// ============================================
// ParseArrayPipe - 문자열 → 배열
// ============================================
@Get()
findAll(@Query('ids', new ParseArrayPipe({ items: Number })) ids: number[]) {}

// ============================================
// ParseUUIDPipe - UUID 검증
// ============================================
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {}

// ============================================
// ParseEnumPipe - Enum 검증
// ============================================
enum OrderStatus {
  PENDING = 'pending',
  COMPLETED = 'completed',
}

@Get()
findAll(@Query('status', new ParseEnumPipe(OrderStatus)) status: OrderStatus) {}

// ============================================
// DefaultValuePipe - 기본값 설정
// ============================================
@Get()
findAll(@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number) {}

// ============================================
// ParseFilePipe - 파일 검증 (파일 업로드)
// ============================================
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(
  @UploadedFile(
    new ParseFilePipe({
      validators: [
        new MaxFileSizeValidator({ maxSize: 1000000 }),
        new FileTypeValidator({ fileType: 'image/jpeg' }),
      ],
    }),
  )
  file: Express.Multer.File,
) {}

 

3.8 Utilities (유틸리티 클래스들)

// ============================================
// Reflector - 메타데이터 읽기
// ============================================
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}
  
  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    const allRoles = this.reflector.getAllAndOverride('roles', [
      context.getHandler(),
      context.getClass(),
    ]);
    return true;
  }
}

// ============================================
// ModuleRef - 동적으로 프로바이더 가져오기
// ============================================
@Injectable()
export class CatsService {
  constructor(private moduleRef: ModuleRef) {}
  
  async onModuleInit() {
    const service = await this.moduleRef.create(SomeService);
    const provider = this.moduleRef.get(SomeProvider);
  }
}

// ============================================
// Logger - 내장 로거
// ============================================
@Injectable()
export class AppService {
  private readonly logger = new Logger(AppService.name);
  
  someMethod() {
    this.logger.log('This is a log message');
    this.logger.error('This is an error message');
    this.logger.warn('This is a warning message');
    this.logger.debug('This is a debug message');
    this.logger.verbose('This is a verbose message');
  }
}

// ============================================
// StreamableFile - 파일 스트리밍
// ============================================
@Get('file')
getFile(): StreamableFile {
  const file = createReadStream(join(process.cwd(), 'package.json'));
  return new StreamableFile(file, {
    type: 'application/json',
    disposition: 'attachment; filename="package.json"',
  });
}

 

3.9 Enums (열거형)

// ============================================
// HttpStatus - HTTP 상태 코드
// ============================================
import { HttpStatus } from '@nestjs/common';

throw new HttpException('Error', HttpStatus.BAD_REQUEST);        // 400
throw new HttpException('Error', HttpStatus.UNAUTHORIZED);       // 401
throw new HttpException('Error', HttpStatus.FORBIDDEN);          // 403
throw new HttpException('Error', HttpStatus.NOT_FOUND);          // 404
throw new HttpException('Error', HttpStatus.INTERNAL_SERVER_ERROR); // 500

// ============================================
// RequestMethod - HTTP 메서드
// ============================================
import { RequestMethod } from '@nestjs/common';

consumer
  .apply(LoggerMiddleware)
  .forRoutes({ path: 'cats', method: RequestMethod.GET });

// ============================================
// Scope - Provider 스코프
// ============================================
import { Scope } from '@nestjs/common';

@Injectable({ scope: Scope.DEFAULT })    // 싱글톤 (기본값)
export class DefaultService {}

@Injectable({ scope: Scope.REQUEST })    // 요청마다 새 인스턴스
export class RequestService {}

@Injectable({ scope: Scope.TRANSIENT })  // 주입될 때마다 새 인스턴스
export class TransientService {}
반응형
Comments