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) // 인증 + 권한 체크
🧱 작동 순서
- 요청 시 JwtAuthGuard가 토큰 유효성 검증
- 이후 RolesGuard가 사용자 권한(Role) 검증
- 실패 시 → 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);
}
🧱 자동 처리 흐름
- 클라이언트가 JSON Body 전송
- @Body()가 DTO에 매핑
- class-validator가 필드 검증
- 통과 시 서비스 로직 실행
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);
}
}
🧱 실행 흐름 요약
- Swagger에서 /billing/confirm API 확인 가능
- JWT 토큰 필수 (@ApiBearerAuth + @UseGuards)
- @Body()로 DTO 검증 후 서비스 호출
- 결과 응답 반환
'Back-end' 카테고리의 다른 글
| Visual Studio Code(VSCODE)에 JAVA 프로젝트 세팅하기 (Feat. Spring, Spring Boot) (0) | 2025.04.02 |
|---|---|
| [Spring/Java] 스프링 시큐리티(Spring Security)를 사용하여 비밀번호 암호화 하기 (5) | 2024.03.15 |
| [SpringBoot] 파일 관련 라이브러리 - Apache Tika (feat. 파일 유효성 체크) (4) | 2024.03.13 |
| [IT] 소프트웨어 디자인 패턴 - MVC 패턴 (30) | 2024.02.15 |
| [Spring] 데이터 전송 - @RequestBody, @RequestPart, JSON, multipart/form-data (2) | 2024.02.01 |