반응형
Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
Tags
- 자바스크립트
- MongoDB
- javascript
- til
- 주간회고
- 생각일기
- mongo
- next.js
- 회고
- Java
- 기록
- 피드백
- js
- 생각정리
- 생각로그
- react
- Git
- CS
- WIL
- 코테
- nest.js
- typescript
- mysql
- 리눅스
- mongoose
- Grafana
- array
- 알고리즘
- 네트워크
- 트러블슈팅
Archives
- Today
- Total
코딩일상
[nest.js] nest.js 뿌수기 공식 docs 모조리 파헤치기[exception-filters] 본문
개발 공부/nest.js
[nest.js] nest.js 뿌수기 공식 docs 모조리 파헤치기[exception-filters]
solutionMan 2025. 12. 16. 23:06반응형

1. Exception Layer
1.1 Built-in Exception Layer
NestJS는 내장 예외 레이어를 제공하여 애플리케이션 전체에서 처리되지 않은 모든 예외를 자동으로 처리합니다.
동작 방식:
- 애플리케이션 코드에서 처리되지 않은 예외 발생
- Built-in global exception filter가 자동으로 캐치
- 적절한 사용자 친화적 응답 자동 전송
1.2 기본 동작
// HttpException 또는 그 서브클래스: 자동으로 적절한 응답 생성
// Unrecognized Exception (HttpException이 아닌 경우):
{
"statusCode": 500,
"message": "Internal server error"
}
중요 포인트:
- http-errors 라이브러리를 부분적으로 지원
- statusCode와 message 속성을 가진 예외는 자동으로 응답에 포함됨
2. HttpException 클래스
2.1 기본 사용법
import { HttpException, HttpStatus } from '@nestjs/common';
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
// 응답:
{
"statusCode": 403,
"message": "Forbidden"
}
2.2 생성자 시그니처
new HttpException(response, status, options?)
파라미터:
- response: string | object
- string: 메시지만 오버라이드
- object: 전체 응답 바디 커스터마이징
- status: HTTP 상태 코드
- HttpStatus enum 사용 권장
- options (선택적):
- cause: Error 객체 (로깅용, 응답에는 미포함)
- description: 에러 설명
2.3 고급 사용 예시
전체 응답 바디 커스터마이징:
@Get()
async findAll() {
try {
await this.service.findAll()
} catch (error) {
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
}, HttpStatus.FORBIDDEN, {
cause: error // 내부 에러 추적용
});
}
}
// 응답:
{
"status": 403,
"error": "This is a custom message"
}
3. Exception Logging
3.1 기본 로깅 동작
// 로그에 출력되지 않는 예외들:
- HttpException (및 상속받은 클래스)
- WsException
- RpcException
// 모두 IntrinsicException을 상속받음
// 이유: 정상적인 애플리케이션 플로우의 일부로 간주
- "왜 HttpException은 기본적으로 로깅되지 않나요?" → 정상적인 비즈니스 로직 흐름의 일부로 간주되기 때문
3.2 커스텀 로깅
로깅이 필요한 경우 커스텀 Exception Filter를 만들어야 함 (후술)
4. Custom Exceptions
4.1 예외 계층 구조 설계
// forbidden.exception.ts
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
// 사용
@Get()
async findAll() {
throw new ForbiddenException();
}
베스트 프랙티스:
- HttpException을 상속받아 예외 계층 구조 생성
- Built-in exception handler가 자동으로 인식
- 타입 안정성 확보
4.2 Built-in HTTP Exceptions
NestJS가 제공하는 표준 예외들:
// 400번대
BadRequestException // 400
UnauthorizedException // 401
NotFoundException // 404
ForbiddenException // 403
NotAcceptableException // 406
RequestTimeoutException // 408
ConflictException // 409
GoneException // 410
PayloadTooLargeException // 413
UnsupportedMediaTypeException // 415
UnprocessableEntityException // 422
ImATeapotException // 418
MethodNotAllowedException // 405
PreconditionFailedException // 412
// 500번대
InternalServerErrorException // 500
NotImplementedException // 501
BadGatewayException // 502
ServiceUnavailableException // 503
GatewayTimeoutException // 504
HttpVersionNotSupportedException // 505
고급 사용:
throw new BadRequestException('Something bad happened', {
cause: new Error(),
description: 'Some error description',
});
// 응답:
{
"message": "Something bad happened",
"error": "Some error description",
"statusCode": 400
}
5. Exception Filters 구현
5.1 기본 필터 구조
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
핵심 요소:
- @Catch(HttpException): 어떤 예외를 처리할지 지정
- ExceptionFilter 인터페이스 구현
- catch() 메서드 구현 필수
5.2 ArgumentsHost 이해
ArgumentsHost란?
- 현재 실행 컨텍스트에 대한 강력한 유틸리티 객체
- HTTP, WebSocket, Microservices 등 모든 컨텍스트에서 작동
- Platform-agnostic 코드 작성 가능
const ctx = host.switchToHttp(); // HTTP 컨텍스트
const request = ctx.getRequest(); // Request 객체
const response = ctx.getResponse(); // Response 객체
// 또는
const wsCtx = host.switchToWs(); // WebSocket
const rpcCtx = host.switchToRpc(); // Microservices
- 직접 Request/Response를 주입받지 않고 ArgumentsHost를 사용하나요?" → Platform-agnostic하게 만들어 HTTP 외에 WebSocket, Microservices에서도 동작하도록
6. Binding Filters (필터 적용 범위)
6.1 Method-scoped (메서드 레벨)
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
// 또는 클래스 전달 (DI 활용, 메모리 효율적)
@Post()
@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
인스턴스 vs 클래스:
- 인스턴스: 즉시 생성, 더 많은 메모리 사용
- 클래스: 프레임워크가 관리, DI 가능, 메모리 효율적 ✅
6.2 Controller-scoped (컨트롤러 레벨)
@Controller('cats')
@UseFilters(new HttpExceptionFilter())
export class CatsController {
// 모든 라우트 핸들러에 적용
}
6.3 Global-scoped (전역)
방법 1: main.ts에서 등록
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
문제점:
- 모듈 컨텍스트 외부에서 등록
- DI(의존성 주입) 불가능 ❌
방법 2: 모듈에서 등록 (추천)
// app.module.ts
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
장점:
- DI 가능 ✅
- 다른 프로바이더 주입 가능
- 진정한 의미의 global filter
고민해볼 사항
- "전역 필터를 등록하는 두 가지 방법의 차이는?" → main.ts는 DI 불가, 모듈 등록은 DI 가능
7. Catch Everything Filter
7.1 모든 예외 캐치
@Catch() // 파라미터 없음 = 모든 예외 캐치
export class CatchEverythingFilter implements ExceptionFilter {
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
catch(exception: unknown, host: ArgumentsHost): void {
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const httpStatus =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const responseBody = {
statusCode: httpStatus,
timestamp: new Date().toISOString(),
path: httpAdapter.getRequestUrl(ctx.getRequest()),
};
httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
}
}
7.2 HTTP Adapter 사용 이유
Platform-agnostic 코드:
// ❌ Platform-specific (Express)
response.status(status).json(body);
// ✅ Platform-agnostic (Express, Fastify 모두 동작)
httpAdapter.reply(response, body, status);
- "HTTP Adapter를 사용하는 이유는?" → Express, Fastify 등 플랫폼 독립적인 코드 작성
7.3 필터 순서 주의사항
// ⚠️ 중요: Catch-all 필터를 먼저 선언
app.useGlobalFilters(new CatchEverythingFilter());
app.useGlobalFilters(new HttpExceptionFilter());
// 특정 타입 필터가 우선 처리되도록
// app.module.ts
// ✅ 최고의 방법: APP_FILTER + 명확한 @Catch
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter, // @Catch(HttpException)
},
{
provide: APP_FILTER,
useClass: AllExceptionFilter, // @Catch()
},
],
})
export class AppModule {}
8. Filter Inheritance (상속)
8.1 BaseExceptionFilter 확장
import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// 커스텀 로직 추가
console.log('Exception caught:', exception);
// 기본 동작 위임
super.catch(exception, host);
}
}
8.2 Global Filter에서 상속 사용
방법 1: HttpAdapter 주입
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));
await app.listen(3000);
}
방법 2: APP_FILTER 토큰 사용
@Module({
providers: [
{
provide: APP_FILTER,
useClass: AllExceptionsFilter,
},
],
})
export class AppModule {}
⚠️ 주의사항:
- Method/Controller-scoped 필터는 new로 인스턴스화하지 말 것
- 프레임워크가 자동으로 인스턴스화하도록 클래스만 전달
9. 리마인드
Q1: Exception Filter의 실행 순서는?
A:
- Method-scoped filter
- Controller-scoped filter
- Global filter
- Built-in global filter
더 구체적인 스코프의 필터가 먼저 실행되며, 예외가 처리되지 않으면 상위 스코프로 전파됩니다.
Q2: Filter에서 DI를 사용하려면?
A: 클래스를 전달하거나 APP_FILTER 토큰을 사용해야 합니다.
// ✅ DI 가능
@UseFilters(HttpExceptionFilter)
// ❌ DI 불가능
@UseFilters(new HttpExceptionFilter())
Q3: ArgumentsHost가 필요한 이유는?
A:
- HTTP, WebSocket, Microservices 등 다양한 컨텍스트에서 동작하는 범용 필터 작성
- Platform-agnostic 코드 구현
- 실행 컨텍스트에 따라 적절한 객체 추출
Q4: IntrinsicException이란?
A: 정상적인 애플리케이션 플로우의 일부로 간주되는 예외들의 기반 클래스입니다.
- HttpException
- WsException
- RpcException
이들은 기본적으로 로깅되지 않습니다.
Q5: Fastify에서 주의할 점은?
A:
// Express
response.json(body);
// Fastify
response.send(body);
// Platform-agnostic
httpAdapter.reply(response, body, status);
Q6: 실무에서 Exception Filter를 어떻게 활용하나요?
A:
- 로깅 및 모니터링: Sentry, DataDog 등과 통합
- 응답 포맷 통일: 회사 API 표준에 맞는 응답 구조
- 에러 추적: requestId, userId 등 컨텍스트 정보 추가
- 알림: 특정 에러 발생 시 Slack, Email 알림
@Catch()
export class SentryExceptionFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// Sentry에 에러 보고
Sentry.captureException(exception);
// 기본 처리
super.catch(exception, host);
}
}
10. 실전 예제
10.1 통합 에러 핸들링 시스템
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
constructor(
private readonly httpAdapterHost: HttpAdapterHost,
private readonly logger: Logger,
) {}
catch(exception: unknown, host: ArgumentsHost): void {
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const request = ctx.getRequest();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message = 'Internal server error';
let errorCode = 'INTERNAL_ERROR';
if (exception instanceof HttpException) {
status = exception.getStatus();
const response = exception.getResponse();
message = typeof response === 'string'
? response
: (response as any).message;
errorCode = (response as any).errorCode || 'HTTP_ERROR';
}
// 로깅
this.logger.error({
statusCode: status,
timestamp: new Date().toISOString(),
path: httpAdapter.getRequestUrl(request),
errorCode,
message,
stack: exception instanceof Error ? exception.stack : undefined,
userId: request.user?.id,
requestId: request.id,
});
// 응답
const responseBody = {
success: false,
errorCode,
message,
timestamp: new Date().toISOString(),
path: httpAdapter.getRequestUrl(request),
};
httpAdapter.reply(ctx.getResponse(), responseBody, status);
}
}
핵심 요약
- Exception Layer: NestJS의 내장 예외 처리 시스템
- HttpException: 표준 HTTP 예외, 커스터마이징 가능
- Exception Filters: 예외 처리 로직 완전 제어
- Binding Scopes: Method → Controller → Global
- ArgumentsHost: Platform-agnostic 컨텍스트 접근
- DI: APP_FILTER 토큰으로 전역 필터에 DI 적용
- Inheritance: BaseExceptionFilter 확장으로 기본 동작 활용
- Catch Everything: @Catch() 파라미터 없이 모든 예외 처리
src/
├── common/
│ ├── exceptions/
│ │ ├── base/
│ │ │ ├── business.exception.ts # 비즈니스 로직 예외 기본 클래스
│ │ │ └── domain.exception.ts # 도메인 예외 기본 클래스
│ │ ├── domain/
│ │ │ ├── user.exception.ts # 사용자 도메인 예외
│ │ │ ├── order.exception.ts # 주문 도메인 예외
│ │ │ └── payment.exception.ts # 결제 도메인 예외
│ │ └── index.ts
│ ├── filters/
│ │ ├── http-exception.filter.ts # HTTP 예외 필터
│ │ ├── all-exception.filter.ts # 전역 예외 필터
│ │ ├── validation-exception.filter.ts # 검증 예외 필터
│ │ └── index.ts
│ ├── interfaces/
│ │ └── error-response.interface.ts # 에러 응답 인터페이스
│ └── constants/
│ └── error-codes.constant.ts # 에러 코드 상수
├── modules/
│ ├── users/
│ │ ├── users.controller.ts
│ │ ├── users.service.ts
│ │ └── dto/
│ └── orders/
│ ├── orders.controller.ts
│ └── orders.service.ts
└── app.module.ts
// src/common/constants/error-codes.constant.ts
export const ERROR_CODES = {
// 공통 에러 (1000번대)
INTERNAL_SERVER_ERROR: 'COMMON-1000',
BAD_REQUEST: 'COMMON-1001',
UNAUTHORIZED: 'COMMON-1002',
FORBIDDEN: 'COMMON-1003',
NOT_FOUND: 'COMMON-1004',
VALIDATION_FAILED: 'COMMON-1005',
// 사용자 도메인 (2000번대)
USER_NOT_FOUND: 'USER-2000',
USER_ALREADY_EXISTS: 'USER-2001',
USER_INACTIVE: 'USER-2002',
INVALID_CREDENTIALS: 'USER-2003',
EMAIL_ALREADY_IN_USE: 'USER-2004',
// 주문 도메인 (3000번대)
ORDER_NOT_FOUND: 'ORDER-3000',
ORDER_ALREADY_CANCELLED: 'ORDER-3001',
ORDER_CANNOT_BE_MODIFIED: 'ORDER-3002',
INSUFFICIENT_STOCK: 'ORDER-3003',
// 결제 도메인 (4000번대)
PAYMENT_FAILED: 'PAYMENT-4000',
PAYMENT_ALREADY_PROCESSED: 'PAYMENT-4001',
INVALID_PAYMENT_METHOD: 'PAYMENT-4002',
INSUFFICIENT_BALANCE: 'PAYMENT-4003',
// 외부 서비스 (5000번대)
EXTERNAL_API_ERROR: 'EXTERNAL-5000',
THIRD_PARTY_SERVICE_UNAVAILABLE: 'EXTERNAL-5001',
} as const;
export type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES];
// src/common/exceptions/base/business.exception.ts
import { HttpException, HttpStatus } from '@nestjs/common';
import { ErrorCode } from '../../constants/error-codes.constant';
export interface BusinessExceptionOptions {
errorCode: ErrorCode;
message: string;
statusCode?: HttpStatus;
details?: any;
cause?: Error;
}
export class BusinessException extends HttpException {
public readonly errorCode: ErrorCode;
public readonly details?: any;
public readonly timestamp: string;
constructor(options: BusinessExceptionOptions) {
const {
errorCode,
message,
statusCode = HttpStatus.BAD_REQUEST,
details,
cause,
} = options;
super(
{
errorCode,
message,
details,
},
statusCode,
{ cause },
);
this.errorCode = errorCode;
this.details = details;
this.timestamp = new Date().toISOString();
// 스택 트레이스 유지
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}
// src/common/filters/http-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { ErrorResponse } from '../interfaces/error-response.interface';
import { BusinessException } from '../exceptions/base/business.exception';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(HttpExceptionFilter.name);
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
const exceptionResponse = exception.getResponse();
// BusinessException 처리
if (exception instanceof BusinessException) {
const errorResponse: ErrorResponse = {
success: false,
errorCode: exception.errorCode,
message: exception.message,
timestamp: exception.timestamp,
path: request.url,
method: request.method,
statusCode: status,
details: exception.details,
traceId: this.getTraceId(request),
};
// 심각한 에러만 로깅 (4xx는 로깅 안함)
if (status >= 500) {
this.logger.error({
...errorResponse,
stack: exception.stack,
cause: exception.cause,
});
} else {
this.logger.warn(errorResponse);
}
return response.status(status).json(errorResponse);
}
// 일반 HttpException 처리
const message =
typeof exceptionResponse === 'string'
? exceptionResponse
: (exceptionResponse as any).message || exception.message;
const errorResponse: ErrorResponse = {
success: false,
errorCode: this.getErrorCode(status),
message: Array.isArray(message) ? message.join(', ') : message,
timestamp: new Date().toISOString(),
path: request.url,
method: request.method,
statusCode: status,
traceId: this.getTraceId(request),
};
this.logger.warn(errorResponse);
response.status(status).json(errorResponse);
}
private getErrorCode(status: number): string {
const errorCodeMap: Record<number, string> = {
400: 'COMMON-1001',
401: 'COMMON-1002',
403: 'COMMON-1003',
404: 'COMMON-1004',
500: 'COMMON-1000',
};
return errorCodeMap[status] || `COMMON-${status}`;
}
private getTraceId(request: Request): string {
// X-Request-ID 헤더 또는 자동 생성
return (
(request.headers['x-request-id'] as string) ||
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
);
}
}
// src/common/filters/all-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
import { ErrorResponse } from '../interfaces/error-response.interface';
import { ERROR_CODES } from '../constants/error-codes.constant';
@Catch()
export class AllExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(AllExceptionFilter.name);
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
catch(exception: unknown, host: ArgumentsHost): void {
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const request = httpAdapter.getRequestUrl(ctx.getRequest());
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message = 'Internal server error';
let errorCode = ERROR_CODES.INTERNAL_SERVER_ERROR;
// HttpException 처리
if (exception instanceof HttpException) {
status = exception.getStatus();
const response = exception.getResponse();
message =
typeof response === 'string'
? response
: (response as any).message || exception.message;
}
// 알 수 없는 에러
else if (exception instanceof Error) {
message = exception.message;
// 프로덕션에서는 상세 에러 숨김
if (process.env.NODE_ENV === 'production') {
message = 'An unexpected error occurred';
}
}
const errorResponse: ErrorResponse = {
success: false,
errorCode,
message,
timestamp: new Date().toISOString(),
path: request,
statusCode: status,
traceId: this.generateTraceId(),
};
// 모든 500 에러는 로깅 (알림 트리거 가능)
if (status >= 500) {
this.logger.error({
...errorResponse,
stack: exception instanceof Error ? exception.stack : undefined,
exception: exception,
});
// TODO: Sentry, DataDog 등으로 알림
// this.notificationService.sendAlert(errorResponse);
}
httpAdapter.reply(ctx.getResponse(), errorResponse, status);
}
private generateTraceId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
// src/modules/users/users.controller.ts
import {
Controller,
Get,
Post,
Body,
Param,
UseFilters,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { ValidationExceptionFilter } from '../../common/filters/validation-exception.filter';
@Controller('users')
// 검증 에러만 컨트롤러 레벨에서 처리
@UseFilters(ValidationExceptionFilter)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get(':id')
async findOne(@Param('id') id: string) {
// 서비스에서 예외 발생 → 자동으로 필터로 전달
return await this.usersService.findOne(id);
}
@Post()
@HttpCode(HttpStatus.CREATED)
async create(@Body() createUserDto: CreateUserDto) {
// 검증 실패 시 ValidationExceptionFilter가 처리
// 이메일 중복 시 EmailAlreadyInUseException 발생 → HttpExceptionFilter가 처리
return await this.usersService.create(createUserDto);
}
@Post('login')
@HttpCode(HttpStatus.OK)
async login(
@Body('email') email: string,
@Body('password') password: string,
) {
// InvalidCredentialsException 발생 가능
const user = await this.usersService.validateCredentials(email, password);
// TODO: JWT 토큰 생성
return { message: 'Login successful', userId: user.id };
}
}
// src/modules/users/dto/create-user.dto.ts
import { IsEmail, IsString, MinLength, MaxLength, IsNotEmpty } from 'class-validator';
export class CreateUserDto {
@IsEmail({}, { message: '올바른 이메일 형식이 아닙니다' })
@IsNotEmpty({ message: '이메일은 필수입니다' })
email: string;
@IsString()
@MinLength(8, { message: '비밀번호는 최소 8자 이상이어야 합니다' })
@MaxLength(20, { message: '비밀번호는 최대 20자까지 가능합니다' })
password: string;
@IsString()
@IsNotEmpty({ message: '이름은 필수입니다' })
@MinLength(2, { message: '이름은 최소 2자 이상이어야 합니다' })
@MaxLength(50, { message: '이름은 최대 50자까지 가능합니다' })
name: string;
}
// src/common/interfaces/error-response.interface.ts
export interface ErrorResponse {
success: false;
errorCode: string;
message: string;
timestamp: string;
path: string;
method?: string;
statusCode: number;
details?: any;
traceId?: string;
}
export interface ValidationErrorDetail {
field: string;
message: string;
value?: any;
}
반응형
'개발 공부 > nest.js' 카테고리의 다른 글
| [nest.js] NestJS 제공 라이브러리 및 데코레이터 완벽 정리 (0) | 2025.12.14 |
|---|---|
| [nest.js] Middleware vs Guards vs Interceptors (0) | 2025.12.14 |
| [nest.js] nest.js 뿌수기 공식 docs 모조리 파헤치기[Middleware] (0) | 2025.12.14 |
| [nest.js] nest.js 뿌수기 공식 docs 모조리 파헤치기[Modules] (0) | 2025.12.11 |
| [nest.js] nest.js 뿌수기 공식 docs 모조리 파헤치기[providers]Constructor-based Injection vs Property-based Injection (0) | 2025.12.11 |
Comments
