네스트는 데코레이터라는 언어 기능을 활용해서 만들어졌습니다.
데코레이터는 많은 프로그래밍 언어에서 사용되는 흔한 컨셉이지만, 자바스크립트에는 비교적 새롭습니다.
데코레이터가 어떻게 동작하는지 좀 더 잘 알아듣기 위해, 이 아티클을 읽어보는 것을 추천합니다.
간단하게 정의하면 아래와 같습니다.
ES2016 데코레이터는 함수를 반환하는 표현식으로, target, name, 그리고 property descriptor를 인자로 받을 수 있다. 데코레이터 앞에 @ 문자를 붙이고 데코레이트 하고자 하는 것의 위에 놓음으로써 적용할 수 있다. 데코레이터는 클래스/메서드/프로퍼티를 위해 정의되고 적용될 수 있다.
Nest는 HTTP 라우트 핸들러와 함께 사용할 수 있는 유용한 파라미터 데코레이터를 제공합니다.
거기에 추가적으로, 직접 커스텀 데코레이터를 만들어서 사용할 수 있습니다.
Node js 세계에서, req 객체에 프로퍼티를 추가적으로 붙이는 일이 굉장히 흔한 일입니다.
이후 이렇게 붙인 프로퍼티를 라우트 핸들러 단에서 추출하여 사용하게 됩니다.
const user = req.user;
코드의 가독성을 높이고, 투명하게 만들기 위해 데코레이터를 만들어 @User()모든 컨트롤러에서 재사용할 수 있습니다.
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
다음과 같은 유저를 사용한다고 가정해봅시다.
{
"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;
},
);
@Get()
async findOne(@User('firstName') firstName: string) {
console.log(`Hello ${firstName}`);
}
기본적으로 Nest는 커스텀 데코레이터에 Pipe를 적용해주지 않기 때문에, 따로 Pipe를 사용하는 설정이 필요합니다.
@Get()
async findOne(
@User(new ValidationPipe({ validateCustomDecorators: true }))
user: UserEntity,
) {
console.log(user);
}
네스트는 여러 데코레이터를 compose 할 수 있는 헬퍼 메서드를 제공해줍니다.
예를 들어, auth에 관련된 모든 데코레이터를 하나로 합치고 싶다면, 이런 방식을 사용할 수 있습니다.
// 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() {}