본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Node.js/Nestjs (Nest.js)

Nestjs 프레임워크 서버(가드와 오류 필터) -7

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2022. 8. 12.

 

#가드(Guard)

가드는 인터셉터와 비슷한 개념으로 컨트롤러에 동작하기 전 사용자가 정의한 행동을 실시 합니다.

가드의 목적은 주로 사용자의 권한(Authentication)을 확인하기 위한 기능으로 사용이 됩니다.

가드를 사용 하려면 CanActivate라는 인터페이스를 상속받아야 합니다.  * 와...CanActivate..앵귤러랑 이름마저 같아!

먼저 가드를 생성하여 줍니다.

* 파일이름 : app.일반가드.ts

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

@Injectable()
export class 일반가드 implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    console.log('알반가드 동작!');
    return true;
  }
}

 

가드에서의 반환값은 3가지를 지원하도록 되어 있습니다.

논리값, 프로미스(논리형태) 그리고 구독가능한 옵저블 형태 입니다.

여기서는 간단하게 논리값을 사용하였습니다.

해당 논리값이 참인 경우에는 해당 페이지로 이동하고 거짓이면 이동하지 않습니다.

 

만약 사용자가 정의한 페이지로 리다이렉트(redirect)를 하려면 오류(익셉션)를 발생시켜 오류를 잡는(catch) 클래스에서 해당 오류타입을 보고 주소를 되돌려주면 됩니다(익셉션 필터는 아래에 소개하고 있습니다)

* 파일이름 : app.일반가드.ts

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

@Injectable()
export class 일반가드 implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    console.log('알반가드 동작!');
    if(원하는권한이 아니라면) throw new UnauthorizedException();  //이렇게 던진후에 오류필터에서 처리합니다.
    return true;
  }
}

 

이렇게 만들어진 가드는 역시나 모듈에 등록해서 사용합니다.

어떤 기능을 만든 뒤에 제어와 권한 역할은 거의 모듈에서 하는 것으로 기억하면 쉬울 것 같습니다!

* 파일이름 : app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { 다른컨트롤러 } from './app.다른컨트롤러';
import { AppService } from './app.service';
import { 디비모듈Module } from './디비모듈/디비모듈.module';
import { 일반인터셉터 } from './app.일반인터셉터';

//모듈인터셉터 설정--
import { 모듈인터셉터 } from './app.모듈인터셉터';
import { APP_INTERCEPTOR } from '@nestjs/core';

import { 일반가드 } from './app.일반가드';


@Module({
  imports: [디비모듈Module],
  controllers: [AppController, 다른컨트롤러],
  providers: [AppService, 일반인터셉터,{ provide: APP_INTERCEPTOR, useClass: 모듈인터셉터 }, 일반가드],
})
export class AppModule {}

 

이제 만들어진 가드를 컨트롤러에 붙여줍니다.

여기서도 간단하게 저번처럼 일반 요청에 대해서 동작하게 하였습니다.

* 파일이름 : app.controller.ts

import { Request, Response } from 'express';
import {
  Controller,
  //생략..
  UseInterceptors,
  UseGuards
} from '@nestjs/common';

import { 일반인터셉터 } from './app.일반인터셉터';
import { 일반가드 } from './app.일반가드';

//생략..

@Controller()
export class AppController {

  //생략..
  
  @UseGuards(일반가드)
  @UseInterceptors(일반인터셉터)
  @Get() //get방식 요청에 응답 합니다. 값이 비어있으면 기본 루트를 의미 합니다.
  getHello(): string {
    return this.appService.getHello();
    
  }
  
}

 

이제 동작하는 모습을 확인 해 보도록 합니다.

오..가장먼저 동작합니다!

 

위 사진처럼 가드는 인터셉터보다도 먼저 동작하게 되어 있습니다.

그리고 가장 먼저동작하고 난 뒤에 다른 행동은 하지 않도록 되어 있습니다.

그렇기 때문에 권한과 같은 행동을 정의할 때 가드를 사용 합니다.

 

가드도 인터셉터와 마찬가지로 모듈, 메인파일에서 동작하게 설정 가능 합니다.

필터, 가드, 인터셉터 등 동작 우선순위는 역시 아래 공식 사이트를 보는게 낫습니다.

https://docs.nestjs.com/faq/request-lifecycle

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 

* 그렇다면 저는 아래처럼 개념을 잡고 개발을 할 것 같습니다!!

 - 사용자의 페이지 접근 권한 확인 : 가드

 - 사용자의 행위 기록(게시글등록, 페이지이동, 로그인 기록, 로그아웃 기록 등) : 인터셉터

 

#오류 필터(Exception filter)

이제 마지막으로 가드 또는 다른 파일에서 오류가 발생한 경우 입니다.

오류를 잡는 클래스는 ExceptionFilter 인터페이스를 상속 받습니다.

간단하게 구현하여 본 오류를 잡는 필터클래스 입니다.

* 파일이름 : Http에러필터.ts

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  Logger,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class Http에러필터 implements ExceptionFilter {
  private readonly 로그기록 = new Logger(HttpException.name);

  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();
    this.로그기록.error(
      `HTTP 익셉션 필터 동작! 에러가 발생한 구간 : ${request.url}`,
    );
    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

 

Catch 데코레이터에 HttpException 클래스를 정의하여 주었습니다.

이제 "Http에러필터" 클래스는 http와 관련된 오류에 동작하게 됩니다.

만약 어딘가로 되돌리고 싶다면 response 객체에 redirect 기능을 사용하여 주면 됩니다.

* 예) response.redirect("주소")

 

이렇게 만든 오류와 관련된 필터는 메인파일에 붙여 줍니다.

* 파일이름 : main.ts

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import { 에러필터 } from './에러필터';
import { Http에러필터 } from './Http에러필터';
import { 글로벌인터셉터 } from './app.글로벌인터셉터';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule); //NestExpressApplication
  app.useGlobalInterceptors(new 글로벌인터셉터());
  app.useGlobalFilters(new 에러필터(), new Http에러필터());
  await app.listen(3000);
}
bootstrap();

 

로거기능을 붙였기 때 문에 오류가 발생하면 아래와 같은 콘솔모습을 볼 수 있습니다.

404오류 잘 나옵니다!

 

Http에러필터 클래스를 보면 코드 중간쯤에는 status라는 변수가 있습니다.

const status = exception.getStatus();

 

해당 오류가 HttpException을 상속받은 경우라면 우리가 알고있는 오류코드 400, 401, 402, 500 등에 대해서 확인이 가능 합니다.

거의 대부분의 오류는 해당 클래스를 상속받고 있습니다.

//const status = exception.getStatus();
const status = exception instanceof HttpException ? 
    exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;   //오류값!

 

오류값을 개발자가 전부 외우는 것은 불가능 합니다.

그러므로 이를 위해서 nestjs에서 제공되는 HttpStatus enum값의 상태 코드를 사용 합니다.

이러한 코드값을 활용 한 다면 좀더 다양한 오류를 처리하는 클래스를 만들거나 또는 조건을 확인 할 수 있습니다.

설마 숫자를 사용하시진 않겠지요?ㅋ

 

이러한 방법을 사용하면 아래처럼 오류 종류에 따라서 다양한 오류필터를 만들어서 사용할 수 있습니다.

왼쪽은 일반서버오류, 오른쪽은 http오류입니다!

 

 

위 내용에 사용한 코드는 아래 제 깃허브에서 받아볼 수 있습니다.

https://github.com/TaeSeungRyu/NestProject/tree/main/step6

 

GitHub - TaeSeungRyu/NestProject: Nestjs 프로젝트

Nestjs 프로젝트. Contribute to TaeSeungRyu/NestProject development by creating an account on GitHub.

github.com

 

공부하면 할 수록 재미있고 즐거운 nestjs!!!

다음 포스팅에서는 세션에 대해서 살펴보겠습니다.

궁금한점 또는 틀린부분은 언제든 연락 주세요!

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글