NestJS Exception Filters 완벽 정리 (라이프사이클부터 내부 동작까지)

2025. 2. 12. 10:36·📂 Backend Engineering
728x90
반응형

NestJS Exception Filters 정리

NestJS에는 애플리케이션 전역에서 처리되지 않은 모든 예외를 담당하는 예외 계층(Exception Layer) 이 내장되어 있습니다.
애플리케이션 코드에서 예외가 처리되지 않고 발생하면, 이 계층이 이를 포착하여 사용자 친화적인 HTTP 응답을 자동으로 반환합니다.

공식 문서: https://docs.nestjs.com/exception-filters


🌠 목차

  • NestJS 요청 라이프사이클
  • Exception Filters의 주요 목적
  • Exception Filters가 에러를 잡을 수 있는 이유
  • 예외 처리 방법들
  • Exception Filters를 활용한 커스텀
  • Filters vs Pipes
  • Interceptor 개념 정리
  • Guard 개념 정리
  • Pipes와 Query / Body 처리 방식

NestJS 요청 라이프사이클

클라이언트 요청이 컨트롤러에 도달하기까지의 흐름은 다음과 같습니다.

  1. 클라이언트 요청
  2. 미들웨어 실행 (로깅, 인증 등)
  3. 가드 실행 (권한 체크: AuthGuard, AdminGuard 등)
  4. 인터셉터 시작
  5. 파이프 실행 (데이터 변환 / 유효성 검사)
  6. 컨트롤러 메서드 실행
  7. 서비스 로직 실행
  8. 인터셉터 종료
  9. 응답 반환

⚠️ 에러가 발생하면?

위 과정 중 어디에서든 예외가 발생하면

  • Exception Filter가 에러를 캐치
  • 적절한 에러 응답을 클라이언트에 반환

Exception Filters의 주요 목적

1️⃣ 에러 캐치

  • 요청 라이프사이클 전역에서 발생한 예외를 처리

2️⃣ 에러 커스텀

  • 에러 응답 포맷을 프로젝트 기준에 맞게 통일
  • 메시지, timestamp, path 등 추가 가능

Exception Filters가 에러를 잡을 수 있는 이유

핵심은 @Catch() 데코레이터입니다.

export declare function Catch(
  ...exceptions: Array<Type<any> | Abstract<any>>
): ClassDecorator;

의미 정리

  • ...exceptions
    • 여러 개의 예외 타입을 Rest Parameter로 전달 가능
  • Type
    • 구체적인 클래스
  • Abstract
    • 추상 클래스 또는 인터페이스
  • 반환값
    • ClassDecorator
// Type 예시
class HttpException {}

// Abstract 예시
abstract class BaseException {}
interface IException {}

👉 즉, 특정 예외 타입만 골라서 필터링 가능합니다.


Node Modules 내부 동작 구조

BaseExceptionFilter

기본 예외 처리 로직을 담당하는 추상 필터 클래스입니다.

export declare class BaseExceptionFilter<T = any>
  implements ExceptionFilter<T> {
  catch(exception: T, host: ArgumentsHost): void;
}
  • 기본 catch() 구현 제공
  • 알 수 없는 에러 처리 로직 포함

BaseExceptionFilterContext

  • 필터 인스턴스 생성 및 관리
  • DI 컨테이너와 연동

ExceptionsHandler

예외 처리 시스템의 중심

  • 발생한 예외를 적절한 필터로 라우팅
export declare class ExceptionsHandler extends BaseExceptionFilter {
  next(exception: any, ctx: ArgumentsHost): void;
}

전체 흐름 요약

예외 발생
  ↓
Router / Middleware (최초 캐치)
  ↓
ExceptionsHandler
  ↓
BaseExceptionFilterContext
  ↓
BaseExceptionFilter or ExternalExceptionsFilter

예외 처리 방법들

기본 제공 방식

throw new HttpException('접근 금지!', HttpStatus.FORBIDDEN);
{
  "statusCode": 403,
  "message": "접근 금지!"
}

하지만 응답을 더 커스터마이징하고 싶다면?

➡️ Exception Filter를 사용합니다.


Exception Filters를 활용한 커스텀

@Catch(HttpException)
export class MyExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const response = host.switchToHttp().getResponse();

    response.status(403).json({
      statusCode: 403,
      timestamp: new Date(),
      message: '죄송합니다. 접근 권한이 없습니다.',
      path: '/admin',
    });
  }
}

언제 쓰면 좋을까?

  • 에러 로깅을 하고 싶을 때
  • 에러 응답 포맷을 통일하고 싶을 때
  • 특정 예외만 다르게 처리하고 싶을 때

여러 예외 처리

@Catch(HttpException, ValidationError)
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {}
}
@Catch()
export class CatchAllFilter implements ExceptionFilter {}

Exception Filters vs Pipes

Exception Filters

  • 발생한 예외를 처리
  • 클라이언트에 반환할 응답을 결정

Pipes

  • 컨트롤러 진입 이전에 실행
  • 데이터 변환 / 유효성 검사

❗ Pipe에서 발생한 예외도 결국 Exception Filter에서 처리됩니다.


Interceptor 정리

역할

  • 메서드 실행 전/후 로직 추가 (AOP)
  • 응답 변환
  • 예외 변환
  • 캐싱, 로깅, 타임아웃 처리

구조

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const now = Date.now();
    console.log('Before...');

    return next.handle().pipe(
      tap(() => console.log(`After... ${Date.now() - now}ms`)),
    );
  }
}

Guard 정리

Guard란?

  • 라우트 핸들러 실행 여부를 결정
  • 권한, 인증, 역할 기반 제어
export interface CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean>;
}

Guard vs Middleware

구분GuardMiddleware

실행 시점 핸들러 직전 라우트 이전
역할 접근 허용 여부 결정 요청/응답 객체 수정
Context ExecutionContext 접근 가능 핸들러 정보 접근 불가

Pipes와 Query / Body 처리

Query String

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {}
{
  "statusCode": 400,
  "message": "Validation failed (numeric string is expected)",
  "error": "Bad Request"
}

👉 잘못된 값이면 컨트롤러 진입 자체를 막음

Body 처리

  • Pipe로 직접 검증 ❌
  • class-validator 사용 ⭕️
export class CreateEventBody {
  @IsUUID()
  uuid?: string;
}

마무리

  • Exception Filter는 에러 처리의 마지막 보루
  • Pipe / Guard / Interceptor와 함께 사용할 때 진짜 위력이 나옵니다

👍 글이 도움이 됐다면 좋아요 한 번 눌러주세요

관심 받는 거 좋아합니다 😄

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'📂 Backend Engineering' 카테고리의 다른 글

(공식문서) NestJS Pipes와 queryString, Body가 데이터 처리하는 방법  (1) 2025.02.18
테스트 코드 현실 적용기: Unit Testing 책을 통한 인사이트와 실무 경험  (0) 2025.01.29
[백엔드]기프티콘 도메인에 대한 이해 & 정리 & 회고  (0) 2024.05.11
[Cursor 페이지네이션] nestJS + Prisma - cursor 페이지네이션 방식 (성능 최적화, 직접 cursor 구현)  (1) 2024.03.31
RDB와 NoSql 비교 및 장점 | AWS에서 제공하는 NoSql은?  (0) 2024.03.15
'📂 Backend Engineering' 카테고리의 다른 글
  • (공식문서) NestJS Pipes와 queryString, Body가 데이터 처리하는 방법
  • 테스트 코드 현실 적용기: Unit Testing 책을 통한 인사이트와 실무 경험
  • [백엔드]기프티콘 도메인에 대한 이해 & 정리 & 회고
  • [Cursor 페이지네이션] nestJS + Prisma - cursor 페이지네이션 방식 (성능 최적화, 직접 cursor 구현)
foodev
foodev
이것저것 개발과 이것저것 리뷰 합니다.
    반응형
    250x250
  • foodev
    개발 개맛집
    foodev
  • 전체
    오늘
    어제
    • 분류 전체보기 (103)
      • ⭐ Featured (4)
      • 📂 Backend Engineering (36)
      • 📂 Troubleshooting & Ops (10)
      • 📂 Infra & System (7)
      • 📂 Reflections (20)
        • Year-in-Review (4)
        • Work & Career (10)
        • Lessons Learned (6)
      • 📂 Team Journal (10)
      • 📂 Archive (16)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    서이추
    인프라 로그 저장 비용 감소하는 방법
    Azure log 비용 줄이기
    typedi란
    QueryDSL
    해피해킹 카라비너
    githubaction 라벨 배포
    db 날림
    스냅샷과히스토리성 차이
    typedi 동작원리
    JPA
    azure ci/cd
    db 초기화
    해피해킹 키매핑
    nestjs pipe
    해피해킹 꿀팁
    nestjs pipe body
    githubaction 라벨 ci/cd
    해피해킹 방향키
    di동작원리
    개발썰
    Azure 로그 최소 저장 30일
    토이프로젝트개발일지
    해피해킹 커스텀
    di란
    di의존성
    스냅샷과 히스토리
    db 날린 썰
    창업패키지후기
    validation failed (numeric string is expected)
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
foodev
NestJS Exception Filters 완벽 정리 (라이프사이클부터 내부 동작까지)
상단으로

티스토리툴바