NestJS 帶你飛! 시리즈 번역 13# Guard

2023. 6. 6. 17:24개발 문서 번역/NestJS

728x90
이 포스팅은 「NestJS 기초실무 가이드 : 강력하고 쉬운 Node.js 웹 프레임워크로 웹사이트 만들기」
(서명: NestJS 基礎必學實務指南:使用強大且易擴展的 Node.js 框架打造網頁應用程式)
책이 출간되었습니다.

 

Guard란 무엇인가요?

 

Guard란 검사 로직인데요. 회사의 보안 시스템과 같습니다. 카드를 통해 출입 통제를 관리한다고 하면

카드를 찍지 않으면 입장을 할 수 없겠지요. 이런 시스템은 주로 신원 확인에 사용됩니다. 
승인되지 않은 요청이 있을 시 Guard는 해당 요청을 막아버립니다. Express의 Guard는 Middleware 레이어에 위치하여 처리하는데요. 이 처리방식이 그렇게 안좋은것만은 아닙니다. 단지 next()를통해 실행되는것이 무엇인지만 잘 명시해준다면요.

Nest는 Guard의 실행순서를 확실히 보장하고자 Guard를 따로 설계하였습니다.

아래는 Guard의 실행 순서가 그려져있습니다.

Guard는 Middleware 다음 Interceptor 전에 실행되는것을 확인할 수 있습니다.

Guard 만들기

Guard는 CLI를 통해 생성할 수 있습니다.

$ nest generate guard <GUARD_NAME>
주의: <GUARD_NAME>은 경로를 포함할 수 있습니다. ex: guards/auth
경로를 포함하여 생성시 src 폴더 안에 경로를 포함하여 Guard가 생성됩니다.

AuthGuardguards 폴더 아래에 생성하겠습니다.

$ nest generate guard guards/auth

src 폴더 아래에 guards라는 폴더 안에 auth.guard.tsauth.guard.spec.ts 파일이 생성된걸 확인할 수 있습니다.

생성된 Guard의 뼈대는 아래와 같은데요.

Guard도 @Injectable 데코레이터가 달려있는걸 확인할 수 있습니다.
그러나 Guard는 CanActivate 인터페이스를 구현해주어야 하며,
canActivate(context: ExecutionContext) 메서드도 작성해주어야 합니다.

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

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

canActivate(context: ExecutionContext) 는 동기와 비동기 모두 지원합니다.
반환값은 boolean, Promise<boolean>이나 Observable<boolean> 중 하나입니다.
인증을 통과하기 위해서는 반드시 최종 결과가 true가 되어야 합니다.

주의: ExecutionContext를 제공해주는 이유는 이를 통해 인증에 필요한 데이터를 가져올 수 있기 때문입니다.

 

Guard 사용하기

사용하기 전에 먼저 auth.guard.ts를 수정하겠습니다.

먼저 이 예제에서는 고의로 false를 반환하고 비동기식으로 처리해보겠습니다.

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

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return of(false).pipe(delay(2000));
  }
}

다음으로 TodoModuleTodoController를 생성해보겠습니다.

$ nest generate module features/todo
$ nest generate controller features/todo
주의: 이미 생성하셨다면 이 단계는 넘어가도 무방합니다.

@UseGuards 데코레이터를 통해 쉽게 적용할 수 있습니다. 사용 방식은 2가지로 나뉩니다.

  • 단일 Resource: Controller 메서드 위에 @UseGuards 데코레이터를 적용시킵니다. 해당 Resource에만 적용이 됩니다.
  • Controller: Controller 위에 직접 @UseGuards 데코레이터를 적용시킵니다. 해당 Controller의 Resource가 전부 적용됩니다.

아래는 Controller의 예제입니다. todo.controller.ts를 수정해보겠습니다.

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../../guards/auth.guard';

@Controller('todos')
@UseGuards(AuthGuard)
export class TodoController {
  @Get()
  getAll() {
    return [];
  }
}

서버를 키고 http://127.0.0.1:3000 에 접속해보면 Guard에 의해 막힌것을 확인할 수 있습니다.

{
  "statusCode": 403,
  "message":"Forbidden resource",
  "error":"Forbidden"
}

 

전역 Guard

해당 App 전체에 Guard를 적용하고자 한다면
main.tsuseGlobalGuards를 추가해주면 적용할 수 있습니다.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AuthGuard } from './guards/auth.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalGuards(new AuthGuard());
  await app.listen(3000);
}
bootstrap();

의존성 주입을 통해 전역 Guard 적용하기

위의 방법대로 모듈 외부에서 전역 설정으로 구현하여도 되지만
Provider의 tokenAPP_GUARD를 설정해줌으로써 구현할 수 있습니다.
이는 useClass를 사용하여 생성해야할 인스턴스의 클래스를 지정해줍니다.

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodoModule } from './features/todo/todo.module';
import { AuthGuard } from './guards/auth.guard';

@Module({
  imports: [TodoModule],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: AuthGuard
    }
  ]
})
export class AppModule {}

 

마치며

Guard는 인증을 진행에있어 좋은 도구중 하나이며 자주 쓰이는 기능 중 하나입니다.
뒤에 포스트에서는 Guard와 관련된 것들이 등장할 예정입니다.
아래는 오늘 포스팅의 요약본입니다.

1. Guard는 Middleware의 후, Interceptor 전에 실행됩니다.

2. Guard는 Middleware보다 더 인증에 특화되어 있습니다.

3. ExecutionContext를 사용하여 인증과 관련된 정보를 얻을 수 있도록 합시다.

4. 전역 Guard는 의존성 주입을 통한 방법으로 구현할 수 있습니다.