10. Custom decorators

사용자 정의 경로 데코레이터

Nest는 데코레이터 라는 언어 기능을 기반으로 구축되었다 . 데코레이터는 일반적으로 사용되는 많은 프로그래밍 언어에서 잘 알려진 개념이지만 JavaScript 세계에서는 여전히 비교적 새로운 개념이다.  간단한 정의는 다음과 같다.

ES2016 데코레이터는 함수를 반환하고 대상, 이름 및 속성 설명자를 인수로 사용할 수 있는 표현식이다. 
장식자 앞에 @문자를 붙이고 장식하려는 항목의 맨 위에 배치하여 적용한다 . 데코레이터는 클래스, 메서드 또는 속성에 대해 정의할 수 있다.

 

매개변수 데코레이터 

Nest는 HTTP 라우트 핸들러와 함께 사용할 수 있는 유용한 매개변수 데코레이터 세트를 제공합니다 . 다음은 제공된 데코레이터와 이들을 나타내는 일반 Express(또는 Fastify) 개체 목록입니다.

@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

 

또한 자기만의 맞춤 데코레이터를 만들 수도 있다. 이것은 매우 유용한데, 이유는 node.js 세계에서는 요청 객체에 속성을 첨부하는 것이 일반적인 관행적이기 때문이다. 다음과 같은 코드를 사용하여 각 경로 처리기에서 수동으로 추출한다.

const user = req.user;

코드를 더 읽기 쉽고 투명하게 만들기 위해  @User() 데코레이터를 만들고 모든 컨트롤러에서 재사용할 수 있다.

// user.decorator.ts

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);
}

 

데이터 전달 

데코레이터의 동작이 일부 조건에 따라 달라지는 경우 data매개변수를 사용하여 데코레이터의 팩토리 함수에 인수를 전달할 수 있다. 이에 대한 한 가지 사용 사례는 요청 개체에서 키를 통해 속성을 추출하는 사용자 지정 데코레이터이다. 예를 들어, 인증 계층이 요청의 유효성을 검사하고 사용자 엔터티를 요청 개체에 연결한다고 가정해 보자. 인증된 요청에 대한 사용자 엔터티는 다음과 같다.

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

속성 이름을 키로 사용하고 연관된 값이 존재하는 경우 해당 값을 반환하는 데코레이터를 정의해 보자. (정의되지 않거나 개체가 생성되지 않은 경우도 마찬가지 ).

// user.decorator.ts

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;
  },
);

컨트롤러의 @User()  데코레이터를 통해 특정 속성에 액세스하는 방법은 다음과 같다 .

@Get()
async findOne(@User('firstName') firstName: string) {
  console.log(`Hello ${firstName}`);
}

이 동일한 데코레이터를 다른 키와 함께 사용하여 다른 속성에 액세스할 수 있다. 객체가 깊거나 복잡한 경우 user요청 핸들러 구현을 더 쉽고 읽기 쉽게 만들 수 있다.

 

힌트) TypeScript 사용자의 경우 createParamDecorator<T>() 이 일반적인 사항입니다. 예를 들어 createParamDecorator<string>((data, ctx) => ...) 선언의 경우, 이는 유형 안전성을 명시적으로 적용할 수 있음을 의미하며 다른 예로 (예: createParamDecorator((data: string, ctx) => ...) 팩토리 함수에 매개변수 유형을 지정할 수 있다. 매개변수 타입을 둘 다 생략하면 data 의 유형은 any 가 됩니다.

 

파이프 작업

Nest는 내장 매개변수( @Body(), @Param()및 @Query())와 동일한 방식으로 사용자 매개변수 데코레이터를 처리한다. 이는 사용자 정의 주석 매개변수(예제에서는 인수 user)에 대해서도 파이프가 실행된다는 것을 의미한다. 또한 파이프를 사용자 정의 데코레이터에 직접 적용할 수도 있다.

@Get()
async findOne(
  @User(new ValidationPipe({ validateCustomDecorators: true }))
  user: UserEntity,
) {
  console.log(user);
}

힌트) validateCustomDecorators 옵션 을 true로 설정해야 한다. ValidationPipe기본적으로 사용자 정의 데코레이터로 주석이 달린 인수의 유효성을 검사하지 않는다.

 

데코레이터 구성 

Nest는 여러 데코레이터를 구성하는 도우미 방법을 제공한다. 예를 들어 인증과 관련된 모든 데코레이터를 단일 데코레이터로 결합한다고 가정해 보면, 이는 다음 구성으로 수행할 수 있다.

// auth.decorator.ts

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

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

이 사용자 정의 @Auth() 데코레이터를 다음과 같이 사용할 수 있다.

@Get('users')
@Auth('admin')
findAllUsers() {}

이는 단일 선언으로 네 개의 데코레이터를 모두 적용하는 효과가 있다.

'Backend(Framework) > NestJS 개요(공식문서 번역)' 카테고리의 다른 글

12. Asynchronous providers  (0) 2023.11.19
11. Custom provider  (1) 2023.11.19
9. Interceptors  (0) 2023.11.19
8. Guards  (0) 2023.11.19
7. Pipes  (0) 2023.11.19
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유