Back-end

NestJS 공부하자! - 데코레이터(어노테이션)

somuxsomu 2025. 10. 28. 12:00

NestJS 데코레이터(어노테이션)

 

회사에서 매번 Spring Boot로 개발을 하다가.. 

Node기반 Next, Nest JS로 개발을 시작하게 되었다..

공부를 하면서 앞으로 하나씩 정리해서 포스팅을 해볼 계획이다! 

 

 

 

🧩 NestJS란?

Node.js 기반의 서버 사이드 프레임워크로,
TypeScript를 완전하게 지원하며

객체지향(OOP), 함수형(FP), 함수형 반응형 프로그래밍(FRP)의

장점을 결합한 구조를 가지고 있다!


NestJS의 가장 큰 특징은 데코레이터(Decorator)기반으로 설계된 프레임워크로,
코드 구조를 간결하게 유지하면서도 명확한 역할 분리를 가능하게 해준다.

 

데코레이터(어노테이션)를 통해 클래스의 역할(Controller, Service, Module 등)을

명확히 구분할 수 있다.

 

 

 

🧠 NestJS의 구조적 특징 🧠

기본적으로 다음과 같은 레이어드 아키텍처(Layered Architecture)를 따른다.

  • Module: 관련 기능을 묶는 단위 
  • Controller: 클라이언트 요청을 받고 응답하는 부분
  • Service: 비즈니스 로직 처리
  • Provider: 의존성 주입(DI)을 통해 재사용 가능한 컴포넌트
  • Decorator: 각 컴포넌트의 역할과 동작을 정의하는 메타데이터

 

NestJS는 규모가 큰 프로젝트에서도 일관된 구조를 유지할 수 있고,
테스트 코드 작성이나 모듈 재사용에도 매우 유리하다.

 

그말은 즉슨 Spring프로젝트와 매우 흡사하다.

그래서 Spring 프레임워크를 사용하던 개발자는 쉽게 배울수 있다는 장점이 있다!

(이 점이 장점이자 단점이 될 수 있는...)

 

 


 ✒️ 목차

  • Swagger 문서화
  • 인증
  • 요청/응답 제어
  • DI
  • 실행 흐름

 

 

1️⃣ Swagger 관련 데코레이터

Swagger는 API 문서를 자동 생성해주는 도구이며,

NestJS에서는 @nestjs/swagger 패키지를 통해 쉽게 설정할 수 있다.

@ApiTags() Swagger 문서의 그룹명 (카테고리) 지정 @ApiTags('Billing')
@ApiOperation() Swagger  문서화:
API의 요약 및 설명 표시
@ApiOperation({ summary: '빌링키 발급', description: 'authKey와 customerKey로 Toss 빌링키 발급' })
@ApiResponse() 응답 상태 코드 및 설명 추가 @ApiResponse({ status: 200, description: '성공' })
@ApiBearerAuth() Swagger 인증 표시:
JWT 인증 필요 API임을 표시
@ApiBearerAuth()
@ApiBody() 요청 Body 구조 지정 @ApiBody({ type: ConfirmBillingDto })
@ApiParam() URL 경로 변수 설명 @ApiParam({ name: 'id', description: '유저 ID' })
@ApiQuery() 쿼리 파라미터 설명 @ApiQuery({ name: 'page', required: false })
@ApiProperty() DTO 필드 문서화 @ApiProperty({ example: 'test@example.com' })

 

🧩 /api-docs (Swagger UI)에서 각 엔드포인트의 설명, 인증, 파라미터를 시각적으로 확인 가능하다.

 

 


 

2️⃣ 인증·인가 관련 데코레이터

@UseGuards() 실제 인증/인가 수행 (JWT, Role 등) @UseGuards(JwtAuthGuard, RolesGuard)
@Roles() 특정 역할만 접근 가능 (커스텀) @Roles('admin')
@Public() 인증 가드에서 제외시킬 엔드포인트 (커스텀) @Public()
@SetMetadata() 커스텀 메타데이터 설정 @SetMetadata('roles', ['admin'])

@UseGuards(JwtAuthGuard)
  - 용도: 실제로 JWT 토큰 검증
  - 효과:
    - 토큰 없으면 → 401 Unauthorized
    - 토큰 유효하지 않으면 → 401 Unauthorized
    - 토큰 만료되면 → 401 Unauthorized
  - 다중 가드 사용:
  @UseGuards(JwtAuthGuard, RolesGuard)  // 인증 + 권한 체크

 

 

🧱 작동 순서

  1. 요청 시 JwtAuthGuard가 토큰 유효성 검증
  2. 이후 RolesGuard가 사용자 권한(Role) 검증
  3. 실패 시 → 401 또는 403 응답 반환

3️⃣ 요청(Request) 관련 데코레이터

@Body() 요청 본문(JSON)을 DTO로 변환 @Body() dto: ConfirmBillingDto
@Query() 쿼리스트링 파라미터 @Query('page') page: number
@Param() URL 경로 파라미터 @Param('id') id: string
@Headers() 요청 헤더 접근 @Headers('authorization') token: string
@Req() Express의 Request 객체 직접 접근 @Req() req: Request
@Ip() 요청자의 IP 주소 추출 @Ip() ip: string

 


4️⃣ 응답(Response) 관련 데코레이터

@HttpCode() 응답 상태코드 변경 @HttpCode(204)
@Header() 응답 헤더 추가 @Header('Cache-Control', 'none')
@Redirect() 리다이렉트 응답 @Redirect('/home', 301)
@Render() 템플릿 렌더링 (SSR) @Render('index')
@Res() Express Response 직접 제어 (비권장) @Res() res: Response

 


 

5️⃣ 의존성 주입 & 모듈 관련 데코레이터

@Controller() 라우팅 컨트롤러 정의 @Controller('billing')
@Injectable() 서비스 클래스 정의 (DI 대상) @Injectable()
@Module() 모듈 단위 정의 @Module({ imports: [], providers: [] })
@Inject() 커스텀 토큰 의존성 주입 @Inject('REDIS_CLIENT') private redis: Redis
@Optional() 선택적 의존성 @Optional() config?: ConfigService

 


6️⃣ 예외, 파이프, 인터셉터, 필터 관련

Exception Filter @Catch() 특정 예외를 잡아 커스텀 응답 반환
Pipe @UsePipes() 요청값 검증, 변환 (ValidationPipe)
Interceptor @UseInterceptors() 요청/응답 가로채기, 로깅/트랜잭션 처리
Filter @UseFilters() 전역 또는 특정 컨트롤러 예외 처리
 
 

7️⃣ 스케줄러 & 마이크로서비스 관련

@Cron() 정해진 시간마다 실행 @Cron('0 0 * * *') // 매일 0시
@Interval() 일정 간격마다 실행 @Interval(10000) // 10초마다
@Timeout() 일정 시간 후 1회 실행 @Timeout(5000)
@EventPattern() 메시지 큐 / 마이크로서비스 이벤트 수신 @Even

 


8️⃣ HTTP 메서드 & 요청 데이터 바인딩

라우팅 메서드

@Get() GET 조회용 API
@Post() POST 생성/등록 요청
@Put() PUT 전체 수정
@Patch() PATCH 부분 수정
@Delete() DELETE 삭제
@All() 모든 메서드 허용 모든 요청 수용
@Controller('billing') 기본 prefix 지정 전체 경로 /billing/...

@Post('confirm')
  - 용도: POST 메서드로 /billing/confirm 엔드포인트 생성
  - 전체 경로: POST /billing/confirm

 

🧩 예시

@Controller('billing') export class 
BillingController { 

@Get('list') getBillingList() { 
	return 'GET /billing/list'; 
} 

@Post('confirm') confirmBilling() { 
	return 'POST /billing/confirm'; 
} }

 

 

요청 데이터 바인딩

@Body() 요청 본문(JSON) → DTO 매핑 @Body() dto: CreateUserDto
@Param() URL 파라미터 @Param('id') id: string
@Query() 쿼리 파라미터 @Query('page') page: number
@Headers() 헤더 값 접근 @Headers('authorization') token: string
@Req() Request 객체 접근 @Req() req: Request
@Res() Response 직접 제어 @Res() res: Response
@Ip() IP 주소 추출 @Ip() ip: string
@Session() 세션 접근 @Session() session: any


  @Body() dto
  - 용도: HTTP 요청 본문(JSON)을 DTO 객체로 변환
  - 자동 처리:
    - JSON 파싱
    - 유효성 검증 (class-validator)
    - 타입 변환 (class-transformer)

 

🧩 DTO + @Body() 예시

export class ConfirmBillingDto {
  @IsString()
  @IsNotEmpty()
  authKey: string;

  @IsString()
  @IsNotEmpty()
  customerKey: string;

  @IsString()
  accountId: string;
}

@Post('confirm')
@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'Toss 빌링키 발급 및 저장' })
@ApiBearerAuth()
async confirm(@Body() dto: ConfirmBillingDto) {
  return this.billingService.confirmBilling(dto);
}

 

🧱 자동 처리 흐름

  1. 클라이언트가 JSON Body 전송
  2. @Body()가 DTO에 매핑
  3. class-validator가 필드 검증
  4. 통과 시 서비스 로직 실행

9️⃣ NestJS 요청 처리 흐름 전체 구조

하나의 요청이 들어올 때 NestJS 내부에서 처리되는 순서이다.

 
Client Request

Middleware (요청 전처리)

Guards (@UseGuards) → 인증 / 권한 검증

Interceptors (@UseInterceptors - before)

Pipes (@UsePipes) → 요청 데이터 검증/변환

Controller → 실제 비즈니스 로직

Service / Repository / DB

Interceptors (@UseInterceptors - after)

Exception Filters (@UseFilters)

Response Sent to Client
 
 

🧱  예시 흐름

POST /billing/confirm → JwtAuthGuard → ValidationPipe(dto) → confirm() 실행 → 응답 반환

 

 

  🧱  실제 요청 흐름
  1. 클라이언트 요청
     POST /billing/confirm
     Authorization: Bearer eyJhbGc...
     Body: { authKey: "...", customerKey: "...", accountId: "..." }

  2. @UseGuards(JwtAuthGuard) 

→ JWT 토큰 검증

  3. @Body() 

→ ConfirmBillingDto 검증
     - @IsString() 체크
     - @IsNotEmpty() 체크

  4. confirm() 메서드 실행

  5. 응답 반환


실전 예시 코드

 

🧩 Toss 결제 등록 예시

@ApiTags('Billing')
@Controller('billing')
export class BillingController {
  constructor(private readonly billingService: BillingService) {}

  @Post('confirm')
  @ApiOperation({ summary: 'authKey + customerKey로 Toss 빌링키 발급 및 저장' })
  @ApiBearerAuth()
  @UseGuards(JwtAuthGuard)
  async confirm(@Body() dto: ConfirmBillingDto) {
    return this.billingService.confirmBilling(dto);
  }
}
 

🧱 실행 흐름 요약

  1. Swagger에서 /billing/confirm API 확인 가능
  2. JWT 토큰 필수 (@ApiBearerAuth + @UseGuards)
  3. @Body()로 DTO 검증 후 서비스 호출
  4. 결과 응답 반환