nestjs) 특정 데이터를 제외하고 response 하는 방법 + custom interceptor

김명성·2022년 11월 19일
1
post-custom-banner

사용자가 아이디를 찾을 때, 데이터에 저장되어 있는 비밀번호나, 특정 데이터를 제외하고 응답하고 싶을 때가 있다.

user.entity.ts

import {AfterInsert, AfterRemove, AfterUpdate, Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

// response에서 제외하게 만들어주는 Exclude
import { Exclude } from 'class-transformer';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  email: string;

  @Column()
  @Exclude()
  password: string;
}

Exclude Decorator를 사용한 뒤, controller에서 Exclude를 사용 할 메서드에 useInterceptors와 classSerializerinterceptor를 사용한다.

  @UseInterceptors(ClassSerializerInterceptor)
  @Get('/:id')
  async findUser(@Param('id') id: string) {
    const user = await this.usersService.findOne(parseInt(id));
    if(!user){
      throw new NotFoundException('user not found.');
    }
    return user;
  }

만약 Exclude 데코레이터만 사용하고, useInterceptors를 사용하지 않는다면 Exclude의 사용과 관계 없이 해당 data가 response에 포함된다.


이 방법에는 단점이 있는데, User entity를 공유하는 여러개의 다른 라우터가 있고,
각 라우터마다 다르게 response를 전달해야 하는 상황에는 유연하게 대처하지 못할 수 있다.
이런 상황이 발생한다면 class-transformer에서 제공하는 ClassSerializerInterceptor가 아닌 직접 커스터마이징한 interceptor를 useInterceptors의 args로 제공해야 한다.

Interceptor 정의.

response로 사용하고 싶은 dto 파일을 생성한 뒤 interceptor에 넣어 실행한다.

serialize.interceptor.ts

import {
  UseInterceptors,
  NestInterceptor,
  ExecutionContext,
  CallHandler
} from '@nestjs/common'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
import { plainToInstance } from 'class-transformer'

export function Serialize(dto: any) {
  return UseInterceptors(new SerializeInterceptor(dto));
}

export class SerializeInterceptor implements NestInterceptor{
  constructor(private dto: any ) {}
  intercept(context: ExecutionContext, handler: CallHandler<any>): Observable<any> | Promise<Observable<any>> {
      // request Handler가 클라이언트의 요청을 처리하기 전에 실행할 코드를 작성한다
      console.log('Im running before the handler');

    // response가 전송되기 전에 실행할 코드를 작성한다.
    return handler.handle().pipe(
      // data는 response data가 들어온다.
      map((data: any) => {
        return plainToInstance(this.dto, data,{
          // 불필요한 값 (response로 내보내지 않을 값)을 제거하는 excludeExtraneousValues
          excludeExtraneousValues: true
        })
      })
    )
  }
}

implaments
implements 는 부모 클래스의 메소드나 변수를 오버라이드(덮어쓰기) 해서 사용 해야 한다.
위 interceptor는 NestInterceptor에 정의된 intercept 메서드를 오버라이드하여 사용했다.

먼저 password를 제외한 id와 password만을 제공할 dto 파일

user.dto.ts

import { Expose } from "class-transformer";

export class UserDto {

  @Expose()
  id: number;

  @Expose()
  email: string;
  
}

custom Interceptor를 Controller 전체에 제공할 수도 있다.

@Controller('auth')
@Serialize(UserDto)
export class UsersController {
  constructor(private usersService:UsersService){}
	//...
}
post-custom-banner

0개의 댓글