Nest.js 개요

3 minute read

Nest.js

  • a framework for building efficient, scalable Node.js server-side applications
  • typescript 사용
  • OOP, FP, FRP 모두 결합
  • default는 싱글톤 패턴을 사용
  • 싱글톤 패턴 : 각 클래스가 하나의 인스턴스만 생성하고, 어떤 프로바이더든지 같은 인스턴스를 공유해 사용하는 패턴
  • init : https://github.com/nestjs/typescript-starter

IoC(Inversion of Control) Container

  • IoC를 구현하는 프레임워크로, 객체를 관리하고 생성을 책임지고 의존성을 관리하는 컨테이너이다.
  • 일반적인 라이브러리 호출과는 다르게, 외부 라이브러리 코드에서 프로그래머가 작성한 코드를 호출하는 형식 (IoC)

Request Lifecycle

  1. incoming request
  2. globally bound middleware
  3. module bound middleware
  4. global guards
  5. controller guards
  6. route guards
  7. global interceptors (pre-controller)
  8. controller interceptors (pre-controller)
  9. route interceptors (pre-controller)
  10. global pipes
  11. controller pipes
  12. route pipes
  13. route parameter pipes
  14. controller (method handler)
  15. service (if exist)
  16. route interceptors (post-request)
  17. controller interceptors (post-request)
  18. global interceptor (post-request) 19 exception filters (route, then controller, then global)
  19. server response

Controllers

screenshot

import { Controller, Get } from '@nestjs/common';

@Controller('cats')
export class CatsController 
  @Post()
  create(): string {
    return 'This action adds a new cat';
  }

  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}
  • routing mechanism (특정 URI에 대한 요청이 여기에 걸림)
  • DTO (Data Transfer Object) 사용

Providers

import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable() /** Nest IOC container에 의해 manage된다는 것을 선언 **/
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}
  • Nest.js의 대부분의 클래스 (services, repositories, …) 등이 provider로써 취급된다.
  • class constructor의 매개변수에 private로 넘겨줌으로써 선언과 초기화를 동시에 할 수 있다.
  • SOLID하게 구현하는 게 좋다.

SOLID (객체지향 개발 5대 원리)

  1. SRP (단일책임의 원칙: Single Responsibility Principle)

    “하나의 클래스를 수정해야되는 이유는 오직 한 가지여야 한다.”

  2. OCP (개방폐쇄의 원칙: Open Close Principle)

    “소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해서는 열려있어야 하고, 수정에 대해서는 닫혀있어야 한다.”

  3. LSP (리스코브 치환의 원칙: The Liskov Substitution Principle)

    “서브 타입은 언제나 자신의 기반 타입(base type) 으로 교체할 수 있어야 한다.”

  4. ISP (인터페이스 분리의 원칙: Interface Segregation Principle)

    “클라이언트가 사용하지 않는 메소드에 영향받지 않아야 한다.”

  5. DIP (의존성역전의 원칙: Dependency Inversion Principle)

    “클래스를 참조할 일이 있을 때 그 클래스를 직접 참조하지 말고 그 대상의 추상 클래스를 만들어서 참조해야 한다”

Guards

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);
  }
}
  • 해당 요청에 적법한 사용자인지를 검사하는 로직이 들어간다. (authentication)
  • true 반환 : request 진행
  • false 반환 : request 거절 (forbiddenException)

Modules

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({ /** decorator provides metadata that Nest makes use of to organize the application structure. **/
  imports: [...],
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService]
})
export class CatsModule {}
  • imports : 해당 모듈에 임포트된 모듈의 리스트
  • controllers : 해당 모듈 내에 정의돼있는, 인스턴스화 해야하는 컨트롤러의 리스트
  • providers: 해당 모듈 내에 nest injector 에 의해 인스턴스화 해야하는 프로바이더의 리스트
  • exports: 해당 모듈을 임모트할 다른 모듈에서 사용되어야 하는 프로바이더의 리스트

Middleware

screenshot

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}
  • req, res 객체를 조작할 수 있다.

Interceptors

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');

    const now = Date.now();
    return next
      .handle() /** If you don't call handle(), the route handler method won't be executed at all. **/
      .pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`)),
      );
  }
}
  • method 실행 전/후 extra 로직을 바인드시킨다.
  • 전 : return 이전
  • 후 : return 문

Pipes

@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}
  • transformation : string -> number 등의 형변환이 필요한 경우 파이프에서 진행
  • validation : 변수 등에 대한 유효성 검증도 할 수 있다.

출처
Nest.js 공식 사이트

Categories:

Updated: