Reflector, ArgumentsHost, ExecutionContext, SetMetadata 정리

조현창·2022년 4월 25일
0

NestJS

목록 보기
2/2

Role 혹은 권한을 통한 인증 구현

Guard의 원리

Interceptor,Filter에서도 사용되지만 복합적인 부분을 사용하는 Guard를 예시로 들어 좀 더 일반적인 지식을 전달하겠습니다.
Guard는 canActive 메소드를 오버라이드 하여 인증 부분을 구현합니다.

import { ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { AuthGuard } from "@nestjs/passport";
import { IS_PUBLIC_KEY } from "src/commons/decorators/public.decorator";

@Injectable()
export class JwtGuard extends AuthGuard("jwt") {
  constructor(private reflector: Reflector) {
    super();
  }

  canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) {
      return true;
    }
    return super.canActivate(context);
  }
}

해당 Guard의 경우 canActive 메소드 마지막에 super 키워드를 활용해서 passport-jwt에서 지원해주는 canActive 메소드를 사용한다.
canActive에서 false를 반환하면 throw UnauthorizedException() 을 실행한다.

우선 프레임워크에서 관리되는 싱글톤 ReflectorcanActive메소드에서 사용하기 위하여 생성자 주입을 통해 reflector를 주입받는다.

  constructor(private reflector: Reflector) {
    super();
  }

reflector를 활용해서 현재 실행중인 context의 metadata들을 조회할 수 있다.

ExecutionContext extends ArgumentHost

@UseGuard(JwtGuard)
@Controller("admins")
export class AdminController {
  constructor(private adminService: AdminService) {}

  @Get("me")
  async getMe(@UserField() user: AdminCredential): Promise<AdminCredential> {
    return user;
  }
}

ArgumentsHost

이와 같은 컨트롤러가 있고 /admins/me 요청이 들어왔다고 하자
controller의 UseGuard(JwtGuard)에서
ArgumentsHost class는

HTTP프로토콜의 매개변수인
req,res,next

혹은

web socket 프로토콜의 매개변수인
client:Socket data:string 처럼
요청에서 전달된 인자를 가지고 있다.

getArgByIndex(0)처럼 매개변수의 인덱스로 특정 매개변수를 가져올 수 있으며 getArgs()로 배열로 반환받을 수도 있다.

switchToHttp(), switchToWs(), switchRTC()를 통해 각 프로토콜별로 매개변수에 대하여 getter, setter 메소드가 정의된 인스턴스로 바꿀 수도 있다.

ExecutionContext

ArgumentsHost클래스를 상속받고 있다. 거기에 더해서 현재 들어온 요청이 도달하는 end point의 controller 클래스와 메소드의 정보를 가지고 있다. getClass()를 통해 클래스의 타입을, getHandler()를 통해 실행되는 메소드를 반환받을 수 있다.

setMetadata()

모두 express에서 router.set()을 사용해보았을 것이다.
setMetadata의 경우 해당 기능과 매우 유사한 역할을 한다고 생각한다.
단 기본적으로 router.set()에서는 같은 key에 입력을 할 시 덮어씌워지지만 setMetadata에서는 해당 ExecutionContext에 각각 저장이 된다.

Reflector

그렇다면 reflector는 어떤 역할을 할까? 바로 Metadata를 조회하는 역할을 한다.
Metadata는 Spring의 Configuration에서 Bean을 문자열 key로 저장하는 것처럼 특정 key에 값을 저장하고 있는 매체이다.
다른 점은 ExecutionContext 인스턴스에서 얻을 수 있는 class와 function을 매개변수로 받아 해당 클래스 혹은 메소드 시점의 metadata를 조회할 수 있다는 것이다.
주요 메소드로는
get<반환자료형>('key', Type(class) or function)
두번째 파라미터로 넘겨진 단일 인스턴스(provider class or method)에 대하여 metadata에서 key를 이용해 value를 반환한다.

getAllAndMerge<반환자료형>('key', [Type(class) or function])
배열에 포함된 인스턴스들에 대하여 해당 key에 할당된 모든 값들을 반환한다.

getAllAndOverride<반환자료형>('key', [Type(class) or function])
배열에 포함된 인스턴스에 대하여 가장 마지막에 할당된 값만을 반환한다.

profile
공부중

0개의 댓글