NestJS Metadata

Outclass·2022년 8월 3일
0

NestJS+GraphQL+TypeORM 

목록 보기
10/16
post-custom-banner

NestJS를 접할 수록 정말 다양한 기능들이 있다는 것에 놀라고 있다. 오늘은 Role구현 등에 이용되는 Metadata를 알아보자

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);
}
  • 기본적으로 Metadata는 컨트롤러에서 사용된다.
  • @SetMetadata() 데코레이터를 통해 사용할 수 있다.
  • Metadata내의 'roles'라는 키가 존재하고, 그 roles중 'admin'의 값을 가진 경우에만 controller를 통과할 수 있다.

코드개선하기

공식문서에서는 코드 가독성 및 재사용성을 높이기 위해 아래와 같이 Custom Decorator를 만들어서 구현하는 것을 권장하고 있다.

//roles.decorator.ts
import { SetMetadata } from '@nestjs/common';

type UserRole = 'guest' | 'admin' | 'user' | 'any'

export const Roles = (roles: UserRole[]) => SetMetadata('roles', roles);
  • type으로 특정 텍스트를 지정해서 제약조건을 만들 수 있다.
//cats.controller.ts
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}
  • custom decorator로 코드의 가독성이 개선된 것을 볼 수 있다.

Guard와 함께 사용

위에서 설정한 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,
    ...
  ]
  ...
})
  • 기존에 생성한 AuthGuard와 함께 구현할 수 있다.
  • Reflector를 불러와서 지정한 key를 통해 값에 접근할 수 있다.
  • 위의 예시들과 같이 구현하면, 지정된 role이 아닌 사용자가 create에 접근할 경우 guard에서 forbidden을 띄우고 접근을 차단한다.

사용후기

잘 활용하면 굉장히 기능적으로 편리할 것 같다고 느꼈다. 특히나 유저의 Role에 따른 특정 기능에 대한 접근 제한을 구현하는데 있어서 아주 복잡한 처리를 하지 않아도 간결하게 구현할 수 있다는 점에서 아주 좋은 기능이라는 생각이 든다.

profile
When you stop having big dreams that’s when you’ve died, despite not being buried yet.
post-custom-banner

0개의 댓글