Back-end

NestJS 공부하자! - 개념 및 데코레이터(Decorator)

somuxsomu 2025. 10. 28. 12:00

NestJS 개념 및 데코레이터(Decorator) 정리

[Spring개발자의 NestJS 적응기]

 

NextJs의 개념과 요청처리 흐름 그리고 핵심이자 Spring의 어노테이션과 비슷한 데코레이터에 대하여 정리를 해보려고 합니다!

 

그동안 회사에서 매번 Spring Boot 환경에서 개발을 하다가..

최근 Node.js 기반의 Next와 NestJS로 개발을 시작하게 되었습니다!

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

 


 

🧩 NestJS란?

 

NestJS는 TypeScript를 지원하는 효율적이고 확장 가능한 Node.js 서버 사이드 프레임워크이다.

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

요소를 결합하여 견고한 아키텍처를 제공한다.


가장 큰 특징은 데코레이터(Decorator)기반으로 설계된 프레임워크이다.
클래스와 메서드의 역할을 명확하게 정의를 하고,

Spring처럼 계층화된 아키텍쳐(Layered Architecture)를 손쉽게 구현할 수 있다.

 

즉, 코드 구조를 간결하게 유지하면서도 명확한 역할 분리를 가능하게 해준다.

 

특징 NestJS Spring Boot
핵심 언어 TypeScript Java / Kotlin
의존성 주입 IoC 컨테이너 (DI) IoC 컨테이너 (DI)
구성 단위 Module Configuration / Package
역할 정의 Decorator (@) Annotation (@)

 


 

🧠 NestJS의 구조적 특징 🧠

NestJs는 일관된 구조를 유지하기 위하여 아래와 같은 레이어드 아키텍처(Layered Architecture)를 따른다.

 

  • Module: 관련 기능을 하나로 묶는 최상위 단위
  • Controller: 클라이언트 요청(Request)을 받고 응답(Response)을 반환
  • Service: 비즈니스 로직 처리
  • Provider: 의존성 주입(DI)되는 컴포넌트(Service, Repository 등)
  • Decorator: 각 컴포넌트의 역할과 동작을 정의하는 메타데이터

 

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

 

한마디로 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️⃣ 인증 및 인가 (Authentication/Authorization)

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

 

@UseGuards(JwtAuthGuard)
  - 용도: 실제로 JWT 토큰 검증
  - 효과:
    a) 토큰 없으면 → 401 Unauthorized
    b) 토큰 유효하지 않으면 → 401 Unauthorized
    c) 토큰 만료되면 → 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 객체로 변환
  - 자동 처리:
    a) JSON 파싱
    b) 유효성 검증 (class-validator)
     c) 타입 변환 (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. 통과 시 서비스 로직 실행

🧱 NestJS 요청 처리 흐름 (Request Lifecycle)

NestJS에서 요청이 들어와 응답이 나가기까지의 순서를 아는 것이 중요하다!

Spring의 Filter -> Interceptor -> Controller 흐름과 매우 유사하다.

 
Incoming Request: 클라이언트 요청

Middleware: 요청 전처리(로그, 공통헤더 등)

Guards (@UseGuards): 인증 및 권한체크

Interceptors (@UseInterceptors - before): 요청 직전 데이터 가공

Pipes (@UsePipes): 입력 데이터 유효성 검증 및 타입 변환 (ValidationPipe)

Controller

Service / Repository / DB

Interceptors (@UseInterceptors - after): 응답 데이터 최종 가공

Exception Filters (@UseFilters): 에러 발생 시 커스텀 예외 처리

Final Response: 클라이언트 응답 전송
 
 

🧱  예시 흐름

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. 결과 응답 반환