개발일지

[NestJS] Interceptor 적용하기 본문

NestJS, Node.js/#01 Project - 투표 커뮤니티

[NestJS] Interceptor 적용하기

lyjin 2022. 10. 27.

Response interface

프론트와의 협업을 위한 response interface를 정의해줬습니다. 초반에는 아래와 같이 response interface 파일을 따로 생성하여 관리했습니다.

 

interface SuccessResponse {
  statusCode: number;
  message: string;
  result?: object;
}

export class GetResponseImpl implements SuccessResponse {
  statusCode: 200;
  message: 'ok';
  result: object;
}

export class PostResponseImpl implements SuccessResponse {
  statusCode: 201;
  message: 'ok';
  result: object;
}

 

@Controller('users')
export class UsersController {
  @Get('profile')
  async getUserProfile(): Promise<GetResponseImpl | ErrorResponse> {
    ...
    return {
      statusCode: 201,
      message: 'ok',
      result: {
        users: user,
      },
    };
  }

 

그러나 http method 하나하나 다 정의해야하고, controller 각 api마다 response 타입을 지정해줘야해하기에 코드가 지저분해진다고 느꼈습니다.

 


Interceptors

NestJS에서 제공하는 미들웨어 중 하나로 다음과 같은 경우 사용합니다.

1. method 전후로 추가 로직이 필요할 때
2. 함수에서 반환된 결과를 변환할 때 ⭐️
3. throw된 예외 변환할 때
4. 기본 기능 동작 확장
5. 특정 조건에 따라 함수를 재정의할 때

반환된 결과를 원하는 형태로 response하기 위한 인터셉터를 정의해줍니다.

 

// common/middleware/http-response.interceptor.ts

export interface Response<T> {
  statusCode: number;
  result?: T;
}

@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor<T, Response<T>> {
  intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<Response<T>> {
    const response = context.switchToHttp().getResponse();
    const statusCode = response?.statusCode;

    return next.handle().pipe(map((result) => ({ statusCode, result })));
  }
}

 

 

생성한 인터셉터를 전역 설정 및 의존성 주입합니다.

 

@Module({
  ...
  providers: [
    AppService,
    {
      provide: APP_INTERCEPTOR,
      useClass: ResponseInterceptor,
    },
  ],
})

 

// 수정된 controller

@Get('profile')
async getUserProfile(): Promise<Users> {
  ...
  return {
    users: await this.usersRepository.findUserByWhereOption(whereOption),
  };
}

 

인터셉터 적용 후 위와 같이 간결해진 것을 확인할 수 있습니다.