개발을 하다 보면 예외 처리의 중요성을 실감하게 된다. 잘못된 입력 데이터, 데이터베이스 연결 오류, 혹은 서버의 예기치 못한 동작은 사용자 경험에 큰 영향을 미친다. NestJS는 이런 문제를 체계적으로 해결할 수 있는 예외 처리 기능을 제공하며, 이를 커스터마이징하면 프로젝트의 요구사항에 맞는 효과적인 처리가 가능한다. 이번 글에서는 NestJS의 예외 처리 메커니즘을 활용해 안정적이고 일관된 예외 처리 방식을 구현하는 방법을 다뤄보겠다.
NestJS는 기본적으로 제공하는 예외 처리 메커니즘 외에 @Catch()
데코레이터를 활용하여 글로벌 예외 필터를 구현할 수 있다.
@Catch()
데코레이터는 NestJS에서 예외 필터를 정의하고 특정 유형의 예외를 처리할 수 있도록 지정하는 데 사용된다. 이 데코레이터를 통해 어떤 예외를 포착하고 처리할 것인지 선언할 수 있다.
@Catch(exceptionType)
형태로 사용하여 특정 예외 타입에 대해 동작하는 필터를 구현할 수 있다.HttpException
유형의 예외만 포착한다.@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
// HttpException 처리 로직
}
}
@Catch()
export class GlobalExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
// 모든 예외 처리 로직
}
}
@Catch()
는 여러 예외를 처리하도록 설정할 수도 있다.@Catch(NotFoundException, UnauthorizedException)
export class SpecificExceptionsFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
// NotFoundException 및 UnauthorizedException 처리
}
}
@Catch()
데코레이터는 특정 예외와 정의된 필터를 연결한다. 이렇게 정의된 필터는 예외가 발생할 때 자동으로 실행된다. 이제 실제로 글로벌 예외 필터를 적용해보자.
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch()
export class GlobalExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost): void {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
// 기본 상태코드가 없는 경우, Internal Server Error로 처리
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
// 기본 메시지를 커스텀 메시지로 설정
const message =
status === HttpStatus.INTERNAL_SERVER_ERROR
? '서버에서 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'
: (exception as HttpException).message ||
'알 수 없는 오류가 발생했습니다.';
// 응답 형식 설정
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message,
});
// 서버 로그 출력
const logger: Logger = new Logger('GlobalExceptionsFilter');
logger.error(exception);
}
}
@Catch()
데코레이터를 통해 GlobalExceptionsFilter
가 모든 예외(unknown
)를 처리할 수 있도록 구현했다. 500 INTERNAL SERVER ERROR
인 경우, '서버에서 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'라는 메시지를 반환한다. 그 외 상태 코드에서는 HttpException
의 메시지 또는 '알 수 없는 오류가 발생했습니다.'라는 메시지를 반환할 것이다.
main.ts에 app.useGlobalFilters(new GlobalExceptionsFilter())
를 추가하면 필터가 전역으로 적용된다. 필터를 적용한 결과는 다음과 같다.
NestJS는 예외 처리를 커스터마이징할 수 있도록 HttpException
클래스를 제공한다. 이를 상속받아 프로젝트에 특화된 예외를 정의하면 특정 상황에서 발생하는 예외를 명확히 구분할 수 있을 뿐만 아니라 반복되는 예외 처리 로직을 줄이고 재사용성을 높일 수 있다.
NestJS의 HttpException
을 상속받아 예외를 정의한다. HttpException
은 두 가지 주요 매개변수를 받는다:
response
: 클라이언트에게 반환할 메시지status
: HTTP 상태 코드import { HttpException, HttpStatus } from '@nestjs/common';
export class NotFoundSessionException extends HttpException {
constructor() {
super('세션 게시물을 찾을 수 없습니다.', HttpStatus.NOT_FOUND);
}
}
export class NotFoundEventException extends HttpException {
constructor() {
super('이벤트를 찾을 수 없습니다.', HttpStatus.NOT_FOUND);
}
}
export class NotFoundUserException extends HttpException {
constructor(userId: number) {
super(
`ID가 ${userId}인 사용자를 찾을 수 없습니다.`,
HttpStatus.NOT_FOUND,
);
}
}
super()
메서드를 호출해 기본 상태 코드(HttpStatus.NOT_FOUND
)와 메시지를 설정하였다. 예외에 더 많은 정보를 포함하고 싶다면, NotFoundUserException
클래스와 같이 생성자에서 매개변수를 받아 처리할 수 있다.
아래와 같이 리포지토리에 커스텀 예외를 적용해 보았다.
import { Prisma } from '@prisma/client';
import { NotFoundSessionException } from '../../../global/exception/custom.exception'
@Injectable()
export class SessionRepository {
async getSession(sessionId: number): Promise<SessionEntity> {
const session: SessionEntity = await this.prisma.session.findUnique({
where: {
id: sessionId,
isDeleted: false,
},
include: {
user: true,
},
});
if (!session) {
throw new NotFoundSessionException();
}
return session;
}
}
필터를 적용한 결과는 다음과 같다.
이번 글에서는 NestJS의 예외 처리 커스터마이징에 대해 살펴보았다. 글로벌 예외 필터로 전역적인 처리 방식을 설정하고, 커스텀 예외 클래스를 통해 모듈별로 특화된 예외를 정의하면 시스템 안정성과 사용자 경험을 동시에 높일 수 있다. 또한 하나의 파일에 여러개의 예외를 한 번에 관리할 수 있어 매우 편리하다. 예외 처리를 커스터마이징 해보면서 그동안 아무렇게나 구현했던 예외 처리도 효율적으로 할 수 있다는 것을 알게 됐다!.!
유익한 내용 잘 읽었습니다