#9 Nestjs Graphql logging과 예외 처리

2023년 08월 31일

어플리케이션에 예외가 일어난 경우 어떤 예외가 발생했는지 아는 것이 중요합니다. NestJS는 이 예외들을 처리해주는 예외 층(layer)이 내장되어 있습니다. Prisma를 사용하고 있기 때문에 Prisma로부터 발생하는 예외를 적절하게 처리하기 위해 prisma exception-filter를 생성해보겠습니다.

예외 필터 생성

excpetion-filters 폴더 하위에 prisma-exception.filter.ts를 생성합니다. 그리고 다음 코드를 작성합니다.

@Catch(Prisma.PrismaClientKnownRequestError)
export class PrismaClientExceptionFilter implements GqlExceptionFilter {
  catch(exception: Prisma.PrismaClientKnownRequestError): any {
    switch (exception.code) {
      case 'P2002': {
        throw new ConflictException('Not Unique Email');
      }
      case 'P2003': {
        throw new UnprocessableEntityException('Entity Not Exist');
      }
      case 'P2025': {
        throw new NotFoundException('Cannot find');
      }
      default:
        break;
    }

    return exception;
  }
}

이 예외 필터는 catch 데코레이터에 전달된 prisma 클라이언트 요청 에러를 잡습니다. 각 prisma 에러 코드에 맞는 알맞는 메시지와 함께 예외를 발생시킬 것입니다.

REST API와는 달리 response 객체를 사용하여 커스텀하고 클라이언트로 전송하지 않습니다. 대신에 http 예외를 발생시켜 GraphQLModule이 이 예외를 클라이언트에 보내주게 됩니다.

마지막으로 main.ts에서 이 필터를 전역으로 사용할 수 있게 만듭니다 - 메소드 혹은 클래스에 국한하여 사용할 수도 있습니다.

async function bootstrap() {
  //..

  app.useGlobalFilters(new PrismaClientExceptionFilter());
  await app.listen(parseInt(port) || 3000);
}

클라이언트를 위한 에러 포맷

클라이언트에 무엇이 잘못했는지 잘 전달하기 위해 응답을 보내기 전에 에러를 커스텀할 수 있습니다.

GraphQLModule 하위에 포맷이 된 에러를 반환하는 formatError 함수를 사용하면 가능합니다.

GraphQLModule.forRootAsync<ApolloDriverConfig>({
  // ...
  useFactory: (config: ConfigService) => {
    return {
      // ...
      formatError: (error) => {
        const originalError = error.extensions
          ?.originalError as OriginalError;

        if (!originalError) {
          return {
            message: error.message,
            code: error.extensions?.code,
          };
        }

        return {
          message: originalError.message,
          code: error.extensions?.code,
        };
      },
    };
  },
  // ...
}),

간결하고 명확함을 위해 필자는 알맞은 메시지 apollo 서버 에러 코드만을 담은 객체를 전송하고자 합니다.

originalError라는 필드를 확인할 수 있습니다. 이 필드는 prisma 예외 필터에서 발생한 예외들에 대한 데이터를 담고 있습니다.

비교

예외 필터와 포맷 에러가 없는 경우

without-formatting

클라이언트는 필요 이상의 정보를 받을 것이고 메시지는 간결하지도 직관적이지도 않습니다.

예외 필터와 포맷 에러가 있는 경우

with-formatting

잘 포맷된 에러와 함께 알맞은 메시지가 전달되는 것을 볼 수 있습니다.

읽어 주셔서 감사합니다. To be continued!

참조