NestJS를 접할 수록 정말 다양한 기능들이 있다는 것에 놀라고 있다. 오늘은 Role구현 등에 이용되는 Metadata를 알아보자
Nest provides the ability to attach custom metadata to route handlers through the @SetMetadata() decorator. We can then access this metadata from within our class to make certain decisions. - Nest공식문서
Meatadata는 루트 핸들러에 첨부되며, 이 Metadata에 접근하면 특정한 결정을 내릴 수 있는 기능을 구현할 수 있다는 이야기이다. 개념적으로 접근하면 다소 난해할 수 있기 때문에 간단한 구현을 통해 이해해보자
Role에 따른 접근 권한 구현을 대표적인 예로 들 수 있다. 아래 구현을 보자
//cats.controller.ts
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
공식문서에서는 코드 가독성 및 재사용성을 높이기 위해 아래와 같이 Custom Decorator를 만들어서 구현하는 것을 권장하고 있다.
//roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
type UserRole = 'guest' | 'admin' | 'user' | 'any'
export const Roles = (roles: UserRole[]) => SetMetadata('roles', roles);
//cats.controller.ts
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
위에서 설정한 Metadata를 이용하여 권한관리 등을 하려면, Guard와 함께 사용해야 한다.
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext) {
//metadata에 저장한 키로 reflector에서 불러오기
const roles = this.reflector.get<AllowedRoles>(
'roles',
context.getHandler(),
);
if (!roles) {
return true;
}
const getContext = GqlExecutionContext.create(context).getContext();
const user = getContext['user'];
if (!user) {
return false;
}
if (roles.includes('Any')) {
return true;
}
return roles.includes(user.role);
}
}
//auth.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';
@Module({
//guard를 app모든 곳에서 사용하고 싶다면
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AuthModule {}
//app.module.ts
@Module({
...
imports : [
...
AuthModule,
...
]
...
})
잘 활용하면 굉장히 기능적으로 편리할 것 같다고 느꼈다. 특히나 유저의 Role에 따른 특정 기능에 대한 접근 제한을 구현하는데 있어서 아주 복잡한 처리를 하지 않아도 간결하게 구현할 수 있다는 점에서 아주 좋은 기능이라는 생각이 든다.