> For the complete documentation index, see [llms.txt](https://jakekwak.gitbook.io/nestjs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jakekwak.gitbook.io/nestjs/overview/guards.md).

# Guards

## Guards

가드는 `@Injectable()` 데코레이터로 주석이 달린 클래스입니다. 가드는`CanActivate` 인터페이스를 구현해야합니다.

![](https://docs.nestjs.com/assets/Guards_1.png)

&#x20;가드는 **단독 책임**이 있습니다. 런타임에 존재하는 특정 조건 (예: 권한, 역할, ACL 등)에 따라 지정된 요청이 라우트 핸들러에 의해 처리될 지 여부를 결정합니다. 이를 종종 **권한**이라고합니다. 인증 (및 일반적으로 공동 작업 인 **인증**)은 일반적으로 기존 Express 응용 프로그램의 [미들웨어](https://app.gitbook.com/middleware)에 의해 처리되었습니다. 토큰 유효성 검사 및 속성을 `요청(Request)`개체에 연결하는 것과 같은 것은 특정 경로 컨텍스트 (및 해당 메타 데이터)와 강력하게 연결되어 있지 않기 때문에 미들웨어는 인증에 적합한 선택입니다.

그러나 미들웨어는 본질적으로 바보입니다. `next()`함수를 호출한 후 어떤 핸들러가 실행될지 알 수 없습니다. 반면에 **Guards**는 `ExecutionContext` 인스턴스에 액세스할 수 있으므로 다음에 무엇이 실행 될지 정확히 알 수 있습니다. 요청/응답 주기의 정확한 시점에 처리 로직을 삽입하고 선언적으로 처리할 수 있도록 예외 필터, 파이프 및 인터셉터와 같이 설계되었습니다. 이렇게 하면 코드를 건조하고 선언적으로 유지할 수 있습니다.

> info **힌트** 가드는 각 미들웨어 **후**에 실행되지만, 인터셉터 나 파이프는 **전**에 실행됩니다.

## Authorization guard

언급 한 바와 같이 **권한**은 발신자 (일반적으로 특정 인증된 사용자)에게 충분한 권한이 있는 경우에만 특정 경로를 사용할 수 있어야 하기 때문에 Guards의 훌륭한 사용 사례입니다. 우리가 구축 할 `AuthGuard`는 이제 인증된 사용자를 가정합니다 (따라서 토큰이 요청 헤더에 첨부되어 있음). 토큰을 추출하고 유효성을 검사하고 추출된 정보를 사용하여 요청을 진행할 수 있는지 여부를 결정합니다.

```typescript
@@filename(auth.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);
  }
}
@@switch
import { Injectable } from '@nestjs/common';

@Injectable()
export class AuthGuard {
  async canActivate(context) {
    const request = context.switchToHttp().getRequest();
    return await validateRequest(request);
  }
}
```

`validateRequest()` 함수 내부의 논리는 필요한 만큼 간단하거나 정교할 수 있습니다. 이 예의 요점은 보호자가 요청/응답 주기에 어떻게 맞는지 보여줍니다.

모든 가드는 `canActivate()`함수를 구현해야합니다. 이 함수는 현재 요청이 허용되는지 여부를 나타내는 조건값을 반환해야 합니다. 응답을 동기식 또는 비동기식으로 반환 할 수 있습니다 (`Promise` 또는 `Observable`을 통해). Nest는 리턴 값을 사용하여 다음 조치를 제어합니다.

* true를 반환하면 요청이 처리됩니다.
* 'false'를 반환하면 Nest는 요청을 거부합니다.

`canActivate()`함수는 `ExecutionContext`인스턴스라는 단일 인수를 취합니다. `ExecutionContext`는 `ArgumentsHost`에서 상속받습니다. 예외 필터 챕터에서 앞서 `ArgumentsHost`를 보았습니다. 여기서는 **original** 핸들러로 전달된 인수를 감싸는 래퍼이며 응용 프로그램의 유형에 따라 다른 인수 배열을 포함합니다. 이 주제에 대한 자세한 내용은 [예외 필터 챕터](https://docs.nestjs.com/exception-filters#arguments-host)을 다시 참조하십시오.

## Execution context

`ExecutionContext`는 `ArgumentsHost`를 확장하여 현재 실행 프로세스에 대한 추가 정보를 제공합니다. 그 모습은 다음과 같습니다.

```typescript
export interface ExecutionContext extends ArgumentsHost {
  getClass<T = any>(): Type<T>;
  getHandler(): Function;
}
```

`getHandler()`메소드는 호출될 핸들러에 대한 참조를 리턴합니다. `getClass()`메소드는 이 특정 핸들러가 속하는 `Controller` 클래스의 유형을 리턴합니다. 예를 들어, 현재 처리 된 요청이 `CatsController`에서 `create()`메소드로 지정된 `POST` 요청 인 경우, `getHandler()`는 `create()` 메소드에 대한 참조를 리턴하고 `getClass()`는 `CatsController` **type** (인스턴스 아님)을 반환합니다.

## Role-based authentication

특정 역할을 가진 사용자만 액세스 할 수 있는 보다 기능적인 보호 기능을 구축해 보겠습니다. 기본 가드 템플릿으로 시작하여 다음 섹션에서 작성합니다. 현재로서는 모든 요청을 진행할 수 있습니다.

```typescript
@@filename(roles.guard)
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}
@@switch
import { Injectable } from '@nestjs/common';

@Injectable()
export class RolesGuard {
  canActivate(context) {
    return true;
  }
}
```

## Binding guards

파이프 및 예외 필터와 마찬가지로 가드는 **컨트롤러 범위(controller-scoped)**, 방법 범위(method-scoped) 또는 전역 범위(global-scoped) 일 수 있습니다. 아래는 `@UseGuards()`데코레이터를 사용하여 컨트롤러 범위의 보호대를 설정했습니다. 이 데코레이터는 단일 인수 또는 쉼표로 구분된 인수 목록을 사용할 수 있습니다. 이를 통해 한 번의 선언으로 적절한 가드 세트를 쉽게 적용할 수 있습니다.

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

> info **힌트** `@UseGuards()`데코레이터는 `@nestjs/common` 패키지에서 가져옵니다.

위에서 우리는 인스턴스 대신에 롤 가드 (RolesGuard) 유형을 전달하여 프레임 워크에 인스턴스화와 의존성 주입을 가능하게 했습니다. 파이프 및 예외 필터와 마찬가지로 내부 인스턴스를 전달할 수도 있습니다.

```typescript
@@filename()
@Controller('cats')
@UseGuards(new RolesGuard())
export class CatsController {}
```

위의 구성은이 컨트롤러가 선언한 모든 핸들러에 가드를 연결합니다. 가드가 단일 메소드에만 적용되도록 하려면 **메소드 수준**에서 `@UseGuards()`데코레이터를 적용합니다.

전역 가드를 설정하려면 Nest 애플리케이션 인스턴스의 `useGlobalGuards()`메소드를 사용하십시오.

```typescript
@@filename()
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());
```

> warning **알림** 하이브리드 앱의 경우 `useGlobalGuards()`메소드는 게이트웨이 및 마이크로 서비스에 대한 보호를 설정하지 않습니다. "표준"(하이브리드가 아닌) 마이크로 서비스 앱의 경우 `useGlobalGuards()`는 가드를 전역으로 마운트합니다.

전역 가드는 모든 컨트롤러와 모든 경로 처리기에 대해 전체 응용 프로그램에서 사용됩니다. 의존성 주입의 관점에서, 모듈 외부에서 등록된 전역 가드 (위의 예에서와 같이 `useGlobalGuards()`로)는 의존성이 주입될 수 없습니다. 이는 모듈의 컨텍스트 밖에서 수행되기 때문입니다. 이 문제를 해결하기 위해 다음 구성을 사용하여 모든 모듈에서 직접 가드를 설정할 수 있습니다.

```typescript
@@filename(app.module)
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';

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

> info **힌트** 가드에 대한 의존성 주입을 수행하기 위해이 접근 방식을 사용할 때, 이 구성이 사용되는 모듈에 관계없이, 가드는 실제로 전역입니다. 어디에서 해야 합니까? 가드 (위 예에서`RolesGuard`)가 정의된 모듈을 선택하십시오. 또한 커스텀 프로 바이더 등록을 다루는 유일한 방법은 `useClass`가 아닙니다. [여기](https://app.gitbook.com/fundamentals/custom-providers)에 대해 자세히 알아보십시오.

## Reflection

우리의 `RolesGuard`가 작동하고 있지만 아직 똑똑하지는 않습니다. 우리는 아직 가장 중요한 보호 기능인 **실행 컨텍스트(execution context)**&#xB97C; 활용하지 않습니다. 아직 역할이나 각 처리기에 허용되는 역할에 대해서는 아직 알지 못합니다. 예를 들어 `CatsController`는 경로마다 다른 권한 체계를 가질 수 있습니다. 일부는 관리자만 사용할 수 있고 다른 일부는 모든 사람에게 공개될 수 있습니다. 유연하고 재사용 가능한 방식으로 역할과 경로를 어떻게 일치시킬 수 있습니까?

**사용자 지정 메타 데이터**가 사용되는 곳입니다. Nest는 `@SetMetadata()`데코레이터를 통해 핸들러를 라우트하기 위해 커스텀 **메타 데이터**를 첨부하는 기능을 제공합니다. 이 메타 데이터는 스마트 가드가 결정을 내려야 하는 누락된 역할 데이터를 제공합니다. `@SetMetadata()`를 사용하여 봅시다 :

```typescript
@@filename(cats.controller)
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}
@@switch
@Post()
@SetMetadata('roles', ['admin'])
@Bind(Body())
async create(createCatDto) {
  this.catsService.create(createCatDto);
}
```

> info **힌트** `@SetMetadata()`데코레이터는`@nestjs/common` 패키지에서 가져옵니다.

위의 구성에서 우리는 `roles` 메타 데이터 (`roles` 가 핵심이고`['admin']`이 특정 값임)를 `create()`메소드에 첨부했습니다. 이것이 작동하는 동안 경로에서 직접 `@SetMetadata()`를 사용하는 것은 좋지 않습니다. 대신 아래와 같이 자신 만의 데코레이터를 만드십시오.

```typescript
@@filename(roles.decorator)
import { SetMetadata } from '@nestjs/common';

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
@@switch
import { SetMetadata } from '@nestjs/common';

export const Roles = (...roles) => SetMetadata('roles', roles);
```

이 접근 방식은 훨씬 깨끗하고 읽기 쉽고 강력하게 입력됩니다. 이제 커스텀 `@Roles()`데코레이터가 있으므로 이를 사용하여 `create()`메소드를 장식 할 수 있습니다.

```typescript
@@filename(cats.controller)
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}
@@switch
@Post()
@Roles('admin')
@Bind(Body())
async create(createCatDto) {
  this.catsService.create(createCatDto);
}
```

## Putting it all together

이제 돌아가서 이것을 `RolesGuard`와 함께 묶어 봅시다. 현재는 모든 경우에 단순히 true를 반환하여 모든 요청을 진행할 수 있습니다. **현재 사용자에게 할당 된 역할**을 처리중인 현재 경로에 필요한 실제 역할과 비교하여 반환 값을 조건부로 만들고 싶습니다. 경로의 역할 (커스텀 메타 데이터)에 액세스하기 위해 프레임 워크에 의해 제공되고`@nestjs/core` 패키지에서 제공되는 `Reflector` 헬퍼 클래스를 사용합니다.

```typescript
@@filename(roles.guard)
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    const hasRole = () => user.roles.some((role) => roles.includes(role));
    return user && user.roles && hasRole();
  }
}
@@switch
import { Injectable, Dependencies } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
@Dependencies(Reflector)
export class RolesGuard {
  constructor(reflector) {
    this.reflector = reflector;
  }

  canActivate(context) {
    const roles = this.reflector.get('roles', context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    const hasRole = () => user.roles.some((role) => roles.includes(role));
    return user && user.roles && hasRole();
  }
}
```

> info **힌트** node.js 세계에서는 권한이 있는 사용자를 `request` 오브젝트에 첨부하는 것이 일반적입니다. 따라서 위의 샘플 코드에서 `request.user`에 사용자 인스턴스와 허용된 역할이 포함되어 있다고 가정합니다. 앱에서 아마도 사용자 지정 **인증 가드(authentication guard)** (또는 미들웨어)에서 해당 연결을 만들 것입니다.

`Reflector` 클래스를 사용하면 지정된 **key**에 의해 메타 데이터에 쉽게 액세스할 수 있습니다 (이 경우 키는 `roles`입니다. `roles.decorator.ts`파일과 거기에서 만들어진 `SetMetadata()`호출을 다시 참조하십시오). 위의 예에서 현재 처리 된 요청 메소드의 메타 데이터를 추출하기 위해 `context.getHandler()`를 전달했습니다. `getHandler()`는 경로 핸들러 함수에 **참조**를 제공한다는 것을 기억하십시오.

**컨트롤러 메타 데이터**를 추출하고 이를 사용하여 현재 사용자 역할을 결정함으로써 이 가드를 보다 일반적으로 만들 수 있습니다. 컨트롤러 메타 데이터를 추출하기 위해 우리는 `context.getHandler()` 대신 `context.getClass()`를 전달합니다:

```typescript
@@filename()
const roles = this.reflector.get<string[]>('roles', context.getClass());
@@switch
const roles = this.reflector.get('roles', context.getClass());
```

권한이 충분하지 않은 사용자가 엔드 포인트를 요청하면 Nest는 다음 응답을 자동으로 반환합니다.

```typescript
{
  "statusCode": 403,
  "message": "Forbidden resource"
}
```

뒤에서 가드가 `false`를 반환하면 프레임 워크에서 `ForbiddenException`이 발생합니다. 다른 오류 응답을 리턴하려면 고유한 예외를 처리해야 합니다. 예를 들면 다음과 같습니다.

```typescript
throw new UnauthorizedException();
```

가드에 의해 발생 된 예외는 [예외 계층](https://app.gitbook.com/exception-filters)(전역 예외 필터 및 현재 컨텍스트에 적용되는 모든 예외 필터)에 의해 처리됩니다.
