2024-03-11 22:25:28

 

프로젝트를 할 때 클라이언트에서 서버로 요청이 왔을 때 클라이언트로 데이터만 보내주면 클라이언트에서 가공해서 사용하기 편하겠지라는 생각에 항상 데이터만 보내주는 식으로 작업을 했습니다.

{
  "Hello World!"
}

 

그렇기에 오류를 확인하기 위해서 에러가 발생한 코드 근처에 console.log를 도배하고 terminal에 어떤 부분 때문에 오류가 발생했는지 찾아보기 일쑤였습니다.

export class TestService() {
  ...
  console.log("여기까지오나?");
  const a = "에러 의심 코드";
  console.log("a = ", a);
  ...
}

 

예외 처리에 대한 부분이 미흡하여 어떤 부분에 대한 오류인지 파악하는데도 시간을 오래 쓰는 거 같아 어떤 식으로 예외 처리를 사용해야 하는지 Nestjs에서 제공해 주는 기본 예외처리 문법에 대해 배워보면서 차근차근 알아보겠습니다.

 

Exceptions Filters

Exceptions filters를 사용하는 주목적은 예외처리입니다. 

예외처리를 통해 클라이언트에게 적절한 응답을 반환해 줄 수 있으며 특정 예외 혹은 예외 그룹을 잡아내는 클래스로 정의됩니다.

 

 

HTTP 상태코드

Filter를 사용할때 상태코드와 상태코드가 어떤 의미를 하는지 알아야 하기 때문에 자주 사용하는 상태코드를 알아보겠습니다.

Number Status Comments
200 OK 요청이 성공적으로 처리되었음을 나타냅니다. 일반적으로 GET 요청의 성공적인 응답으로 사용됩니다.
201 Created 요청이 성공적으로 이행되었으며 새로운 리소스가 생성되었음을 나타냅니다. 주로 POST 요청으로 새로운 엔트리를 생성할 때 사용됩니다.
400 Bad Request 서버가 요청을 이해할 수 없음을 나타냅니다. 클라이언트의 요청이 잘못 구성되어 있을 때 반환합니다.
401 Unauthorized 요청이 인증을 필요로 하며, 클라이언트가 그 인증을 제공하지 않았을 때 사용됩니다.
403 Forbidden 서버가 요청을 이해했지만 권한 부족으로 인해 거부됩니다.
404 Not Found 서버가 요청받은 리소스를 찾을 수 없을 때 사용됩니다.
500 Internal Server Error 서버가 요청을 처리할 수 없는 상황이 발생했을 때 사용됩니다. 서버 내부의 오류를 나타냅니다.

 

 

HttpExceptionFilter

기본으로 build-in 되어 있는 Filter가 있지만 내가 원하는 형식으로 만들어서 사용하기 쉽기 때문에 기본 코드를 통해 Filter를 알아보겠습니다. 해당 코드는 Nestjs 공식 문서에도 나와있는 코드로 기본 코드이며 해당 코드를 보며 어떤 코드로 구성되어 있는지 알아보겠습니다.

filter 디렉토리와 http-exception.filter.ts 파일 생성

// http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
// express 프레임워크에서 사용했던 Request, Response 객체의 타입
import { Request, Response } from 'express';

// 해당 데코레이터는 HttpException 예외와 그 서브 클래스의 예외를 캐치하여 처리하도록 클래스 지정
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  // 예외가 발생했을때 호출될 메소드로 예외 객체와 요청의 컨텍스트를 인자로 받음
  catch(exception: HttpException, host: ArgumentsHost) {
    // HTTP 기반의 요청 컨텍스트를 가져와 Request와 Response 객체에 접근
    const ctx = host.switchToHttp();
    // 가져온 컨텍스트에서 Reponse 객체를 가져옴
    const response = ctx.getResponse<Response>();
    // 가져온 컨텍스트에서 Request 객체를 가져옴
    const request = ctx.getRequest<Request>();
    // HTTP 상태 코드를 가져옴
    const status = exception.getStatus();

    response
      .status(status) // 응답에 상태 코드를 설정
      .json({ // JSON 형식으로 반환
        statusCode: status, // 예외 상태 코드
        timestamp: new Date().toISOString(), // 현재 시각
        path: request.url, // 요청된 URL 경로
      });
  }
}

 

Filter의 종류

Filter는 어디에 주입하는지에 따라 다르게 동작합니다.

 

1. Method Scope 

특정 컨트롤러의 메서드에만 Filter를 적용합니다.

// app.controller.ts
import { Controller, Get, HttpException, UseFilters } from '@nestjs/common';
import { AppService } from './app.service';
import { HttpExceptionFilter } from './filters/http-exception.filter';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/method-scope-test')
  // 해당 method에만 필터가 적용
  @UseFilters(HttpExceptionFilter)
  MethodScopeTest() {
    throw new HttpException('Forbidden', 403);
  }
}

 

2. Contoller Scope

특정 컨트롤러의 모든 메서드에 Filter를 적용합니다.

// app.controller.ts
import { Controller, Get, HttpException, UseFilters } from '@nestjs/common';
import { AppService } from './app.service';
import { HttpExceptionFilter } from './filters/http-exception.filter';

@Controller()
// 해당 컨트롤러 전체에 필터 적용
@UseFilters(HttpExceptionFilter)
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/method-scope-test')
  MethodScopeTest() {
    throw new HttpException('Forbidden', 403);
  }
}

 

3. Global Scope

애플리케이션 전역에 Filter를 적용할 수도 있고 특정 Module에 글로벌하게 적용할 수 있습니다.

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './http-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HttpExceptionFilter } from './http-exception.filter';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
  ],
})
export class AppModule {}

 

 

Filter가 적용되기 전 

 

Filter가 적용된 후

 

위의 설정을 추가하여 예외처리 결과가 바뀌는 것을 확인할 수 있습니다.

 

예외처리는 service에서 한다고만 생각하고 따로 관리를 해야겠다는 생각을 해본 적이 없었고

controller는 단순 route다라고 생각하였는데 그 이외에도 많은 기능들이 있다는 걸 공식 문서를 통해 알게 되었습니다.

728x90

'Nestjs' 카테고리의 다른 글

[NestJs] Interceptors 알아보기  (0) 2024.03.14
[NestJs] Guards 알아보기  (0) 2024.03.13
[NestJs] Pipes 알아보기  (1) 2024.03.12
[NestJs] 기본 구조 살펴보기  (0) 2024.03.10
[NestJs] NestJs 알아보기  (0) 2024.03.10