처음부터 차근차근

[NestJS] Custom decorator 본문

FrameWork/NestJS

[NestJS] Custom decorator

HangJu_95 2023. 11. 28. 14:33
728x90
NestJS는 decorator라는 기능으로 구성되어 있습니다.

Decorator는 여러 프로그래밍 언어에서는 많이 알려져있지만, Javascript에서는 생소합니다. 이 부분은 Typescript 에서 자세히 다룹니다.

Param decorators

Nest에서는 Http route handler와 같이 사용할 수 있는 param decorator를 제공합니다.

우리가 만약 Request 객체에서 User를 받아올려고 한다면, @Body() user:userEntity를 사용해야 합니다.

@Request(), @Req() req
@Response(), @Res() res
@Next() next
@Session() req.session
@Param(param?: string) req.params / req.params[param]
@Body(param?: string) req.body / req.body[param]
@Query(param?: string) req.query / req.query[param]
@Headers(param?: string) req.headers / req.headers[param]
@Ip() req.ip
@HostParam() req.hosts

그러나 우리는 직접 데코레이터를 만들 수 있습니다.

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);
@Get()
async findOne(@User() user: UserEntity) {
  console.log(user);
}

하지만 이렇게 데코레이터를 만들어 적용한다면, 간단하게 구상할 수 있습니다.

(마치 @Body()에 있는 데이터를 받아오는 것입니다.)

Passing Data

이번에는 특정 조건에 따라 다른 처리를 할 수 있도록 하는 데코레이터를 만들어보겠습니다.

우리가 이러한 요청을 받는다고 생각해봅시다.

{
  "id": 101,
  "firstName": "Alan",
  "lastName": "Turing",
  "email": "alan@email.com",
  "roles": ["admin"]
}

이러한 userEntity가 있을 때, 원하는 속성만 받아올 수 있도록 해보겠습니다.

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;

    return data ? user?.[data] : user;
  },
);
@Get()
async findOne(@User('firstName') firstName: string) {
  console.log(`Hello ${firstName}`);
}

해당 data를 통해서 요청 객체의 특정 데이터를 키 값으로 추출할 수 있게 만들었습니다.

위와 같이 @User('firstName') 데코레이터를 통해 request.user.firstName만 추출할 수 있습니다.

typescript 환경에서는 createParamDecorator<T>() 는 generic이기 때문에 data 매개 변수의 타입을 강제할 수 있습니다.

Working with pipes

custom param decorator에는 동일하게 pipe를 적용할 수 있습니다.

다만, custom decorator는 pipe를 적용하기 위해서는 validateCustomDecorators 옵션이 true이어야 합니다.

Decorator composition

데코레이터를 사용하다보면, 수많은 데코레이터를 쌓인 것을 볼 수 있습니다.

하나의 예시로, NestJS를 들으면서 강의 내용을 따라해 본 결과, Decorator가 많이 쌓인 걸 확인할 수 있습니다.

  @ApiBearerAuth()
  @ApiGetItemsResponse(FindUserResDto)
  @Roles(Role.Admin)
  @Get()
  @UseGuards(JwtAuthGuard)
  // 비동기 처리를 위해 Promise로 데이터를 받는다.
  async findAll(@Query() { page, size }: PageReqDto): Promise<FindUserResDto[]> {
    // 전체 유저수 확인 메서드
    // pagenation을 위해 Page Dto로 데이터를 받는다.
    const users = await this.userService.findAll(page, size);
    return users.map(({ id, email, createdAt }) => {
      return { id, email, createdAt: createdAt.toISOString() };
    });
  }

이러한 부분은 Composition을 통해 정리할 수 있습니다.

import { applyDecorators } from '@nestjs/common';

export function Auth(...roles: Role[]) {
  return applyDecorators(
    SetMetadata('roles', roles),
    UseGuards(AuthGuard, RolesGuard),
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Unauthorized' }),
  );
}
@Get('users')
@Auth('admin')
findAllUsers() {}

참조

https://gongmeda.tistory.com/58

https://docs.nestjs.com/custom-decorators

'FrameWork > NestJS' 카테고리의 다른 글

[NestJS] NestJS에서 GraphQL 초기 설정  (1) 2023.11.29
[NestJS] 제어 역전과 의존성 주입  (1) 2023.11.29
[NestJS] Exception filter  (0) 2023.11.28
[NestJS] Pipe  (0) 2023.11.28
[NestJS] interceptor  (0) 2023.11.27