NestJS Interceptor 알아보기

임홍원·2022년 3월 17일
1

NestJS의 Interceptor 이해를 위해 이 글을 작성한다.

Nest의 공식문서에서 Interceptor는 @Injectable() 데코레이터로 annotate 되는 클래스이다. NestInterceptor 인터페이스를 반드시 implement 해야 한다.

Interceptor 는 관점 지향 프로그래밍(AOP) 기술에서 많은 영감을 받아서 만들어진 좋은 다양한 기능을 가진 집합이다. Interceptors는 다음과 같은 것들을 할 수 있다.

  1. 메소드 실행 전/후에 다른 logic을 bind
  2. 함수에서 리턴된 값을 변형
  3. 함수에서 throw 된 예외를 변형
  4. 기본 함수의 기능을 extend
  5. 특정 조건 하에서 함수를 완전히 override

라고 기술되어있다.

코드로 살펴보면

import {
    CallHandler,
    ExecutionContext,
    Injectable,
    NestInterceptor,
} from '@nestjs/common';
import { Observable, map } from 'rxjs';

@Injectable()
export class UndefinedToNullInterceptor implements NestInterceptor {
    intercept(
        context: ExecutionContext,
        next: CallHandler<any>,
    ): Observable<any> | Promise<Observable<any>> {
        //Controller 가기 전 부분
        return (
            next
                .handle()
                //handle()다음부분은 Controller 실행되고 난 후 부분
                .pipe(map((data) => (data === undefined ? null : data)))
        );
    }
}

위 코드를 살펴보면 Interceptor로 지정할 Class는 꼭 Implements를 해주어야 한다.
Interceptor는 두개의 인자 (매개변수)를 가지는 intercept 메서드를 포함하고 있다.
첫번째 인자인 context는 여러가지 기능들을 제공하고
두번째 인자인 CallHanlder는 route handler의 메서드를 호출할 때 쓰는 handle() 메서드를 포함하고 있다.

주석을 살펴보면 return 전 부분에 코드를 넣게 되면 Controller로 가기 전 데이터를 조작, 변형 할 수 있다.
handle() 메서드 다음 실행되는 부분은 Controller가 실행되고 난 부분의 데이터를 조작, 변형 할 수 있다.

위 코드는 data가 undefined이면 null로 변경하고 아니면 data를 그냥 보내는 코드이다.

Interceptor는 마지막에 데이터를 한번 더 가공할 때 주로 사용한다.

pipe에는 Error를 넣을 수 도 있는데 그것은 Exceptionfilter로 알아보자.

import {
    Body,
    Controller,
    Get,
    HttpException,
    Post,
    Req,
    Res,
    UseInterceptors,
} from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { User } from 'src/common/decorator/user.decorator';
import { UserDto } from 'src/common/dto/user.dto';
import { UndefinedToNullInterceptor } from 'src/common/interceptors/undefinedToNull.interceptor';
import { JoinRequestsDto } from './dto/join.request.dto';
import { UsersService } from './users.service';

@UseInterceptors(UndefinedToNullInterceptor)
@ApiTags('USER')
@Controller('api/users')
export class UsersController {
    constructor(private usersService: UsersService) {}
	
  	//@UseInterceptors(UndefinedToNullInterceptor) 개별 Route Interceptor
    @ApiResponse({
        type: UserDto,
    })
    @ApiOperation({ summary: '내 정보 조회' })
    @Get()
    getUsers(@User() user) {
        return user;
    }

    
    }

Interceptor는 장착을 해주어야 하는데 Controller에 @UseInterceptors() 데코레이터로 장착하면 된다.

전체 Controller에 Interceptor에 적용하거나 개별 Router에도 적용할 수 있다.

0개의 댓글