본문 바로가기
개발자 도전기/[STUDY] JS || TS

NestJS | docs | Guards

by 답수 2023. 2. 24.
728x90
SMALL

 

 

Guards

가드는 @Injectable() 데코레이터를 주석으로 다는 클래스이고,  CanActivate 인터페이스를 implements 한다. 가드는 단일 책임(single responsibility)를 가진다. 이는 특정 상황에 따라 주어진 요청이 라우트 핸들러에 의해 다뤄지는지 결정한다. 가드는 인가(authorization)에 주로 사용된다.

 

Express에서 인증 및 인가는 미들웨어에서 처리한다. 하지만 미들웨어로 인증 처리를 하면 next() 호출 이후에 어떤 핸들러가 실행되는지 모른다는 단점이 있다. 반면에 가드는 ExecutionContext 인스턴스에 접근하여 명확하게 다음에 실행되는 것이 무엇인지 알 수 있다고 한다. 즉 가드도 예외 필터나 파이프, 인터셉터처럼 요청/응답 사이클에서 더 명확한 곳에서 로직이 실행될 수 있도록 디자인되었다.

(가드는 모든 미들웨어 이후에 실행이 되고, 인터셉터와 파이프 이전에 실행된다고 한다. 이 순서를 잘 알아야 할 듯)

 

Authorization guard

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return validateRequest(request);
  }
}

위에서 말했던 것처럼 AuthGuard 클래스를 CanActivate 인터페이스로 implements하여 작성할 수 있다. 즉 모든 가드는 canActivate() 함수를 구현해야 한다.

 

Execution context

canActivate() 함수는 ExecutionContext 인스턴스라는 하나의 인자를 받는다. ExecutionContext 인스턴스는 ArgumentsHost 로부터 상속받는다(이는 이전에 예외처리할 때 다뤘었다).

 

이외에 특수한 역할을 가진 유저만 접근할 수 있는 role-based authentication guard도 있다.

 

Binding guards

가드도 예외처리 필터나 파이프와 마찬가지로 컨트롤러 스코프나 메서드, 혹은 글로벌 스코프에 적용될 수 있다. 컨트롤러단에서는 @UseGuards() 데코레이터를 사용하면 된다. 이 데코레이터도 @nestjs/common 패키지에서 받아올 수 있다.

@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {}

 

만약 글로벌 가드를 사용하고 싶다면 useGlobalGuards() 메서드를 이용하면 된다.

const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());

하지만 의존성 주입 관점에서 모듈 외부에서 등록된 글로벙 가드는 어떤 모듈의 컨텍스트도 아닌 외부에서 수행되기 때문에 의존성을 주입할 수 없다. 이 문제를 해결하기 위해서는 다음과 같이 사용하여 모듈에 직접 가드를 설정할 수 있다.

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})
export class AppModule {}

 

하지만 뭔가 살짝 아쉽다. RolesGuard는 잘 작동하겠지만 중요한 가드 기능을 활용하지 못하고 있다(execution context). 예컨대 CatsController는 다른 라우트에 대해 다른 권한 체계를 가질 수 있고, 일부는 관리자에게만 허용되고 다른 핸들러는 모든 사용자가 사용하게 될 수 있다. 

더보기

execution context

Nest는 여러 애플리케이션 컨텍스트(e.g. Nest HTTP server-based, microservices, WebSockets application context)에서 작동하는 애플리케이션을 쉽게 작성할 수 있도록 도와주는 유틸리티 클래스를 제공한다. 이 유틸리티는 광범위한 컨트롤러, 메서드 및 실행 컨텍스트에서 작동할 수 있는 가드, 필터, 인터셉터를 빌드하는 데 사용할 수 있는 현재 실행 컨텍스트에 대한 정보를 제공한다.

ref: https://docs.nestjs.com/fundamentals/execution-context

@SetMetadata() 데코레이터를 통해 라우트 핸들러에 사용자 지정 메타데이터를 추가할 수 있다. 이 메타데이터는 스마트 가드가 결정을 내리는 데 필요한 누락된 역할 데이터를 제공한다. 

@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

 

 

 

 

728x90
LIST

댓글