사용자가 아이디를 찾을 때, 데이터에 저장되어 있는 비밀번호나, 특정 데이터를 제외하고 응답하고 싶을 때가 있다.
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로 제공해야 한다.
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){}
//...
}