Nest.js 리뷰

영태·2022년 8월 7일
4

[REVIEW]

목록 보기
2/7
post-thumbnail

Nest.js

nest.js는 제가 백엔드 개발을 시작하고 가장 처음 접한 프레임워크이자 가장 많이 사용한 프레임워크입니다.
지금으로서는 이 nest.js를 통해 서버를 만드는데 익숙해졌지만, nest.js를 왜 사용하고 어떤 점이 장점인지 고민해본 기억이 적은 것 같습니다
따라서 nest.js의 특성과 장단점을 적어보고 이 프레임워크를 사용한 이유와 사용했던 경험에 대해 리뷰해볼 생각입니다

Nest.js란?

  • Express.js와 같은 서버 프레임워크 중에 하나입니다
  • Express 또는 Fastify 프레임워크를 wrapping하여 동작합니다. 기본으로 설치하면 Express를 사용합니다.
  • node.js 진영에 있어서 서버 측 어플리케이션을 구축하기 위해 제작된 프레임워크
  • TS 기반의 객체 지향 프로그래밍, 함수형 프로그래밍, FPR(Functional Reactive Programming)을 지원합니다
  • 다양한 기능들을 자유롭게 정형화/구조화하여 설계할 수 있지만, 대부분 클래스를 통해 설계가 됩니다. 따라서 JS,TS의 클래스에 대한 확실한 이해도가 필요합니다.
  • 싱글톤 패턴을 지향합니다. 인스턴스를 직접 생성하지 않고 모듈을 통해 inject하는 패턴을 권장하고 있습니다

그럼 프레임워크는 뭔가요? 라이브러리와 다른 점은?

  • 프레임워크와 라이브러리 둘 다 공통으로 사용될 수 있는 기능들을 모듈화한 것을 의미합니다
  • 라이브러리
    - 폴더명, 파일명 등에 대한 규칙이 없습니다
    • 프레임워크에 비해 자유롭습니다
    • 도구로 비유하자면 다양한 공구들이라고 비유할 수 있습니다
    • 가위, 망치와 같은 도구를 스스로 직접 컨트롤해야 합니다
  • 프레임 워크
    - 폴더명, 파일명 등에 대한 규칙이 있습니다
    • 라이브러리에 비해 보다 엄격합니다
    • 도구로 비유하자면 모든 기능이 갖추어진 비행기와 같다고 할 수 있습니다
    • 설정을 해놓으면 스스로 움직이듯 자동화가 되어있고 그것에 탑승하는 개념이라고 볼 수 있습니다

왜 Nest.js인가?

nest.js는 node.js를 이용한 서버 개발에 있어서 아키텍처의 문제를 해결하기 위한 프레임워크입니다
node.js 진여의 서버 개발에 있어서 이미 사용하기 쉽고 성능이 뛰어난 Express.js가 존재합니다. 그러나 지나치게 자유롭다는 단점이 있습니다

Express.js의 장점과 단점

  • 아키텍처, 즉 구조에 관한 정의나 추가적인 기능을 제공하지 않습니다.
    []
    • 다음 사진과 같이 사람 성향에 따라서 각기 다른 다양한 구조로 개발을 합니다
    • 이렇게 되면 다른 구조를 이해하기 위한 비용 혹은 아키텍처를 새롭게 선정해야 하는 비용이 듭니다
    • nest.js는 아키텍처에 대한 정의를 제공해버리기 때문에 다른 개발자가 작성한 코드를 쉽게 이해할 수 있다는 장점이 잇습니다

아래는 yukina1418의 벨로그에 정리가 너무 잘되어있어서 발췌한 내용입니다

  • JS기반에 커뮤니티가 많이 활성화 되어있어서 진입장벽이 낮다.
    • 앞으로 나올 두개의 프레임워크에 비하면 말도 안되게 많다.
  • 최소한의 것만 지원하여 Micro Framework라고 불린다.
    • 필요한 것이 있다면 모두 찾아서 라이브러리를 깔아야한다.
  • 정해져있는 추상화 계층이 존재하지 않는다.
    • 그렇기에 express의 아키텍쳐 설계를 개발자가 직접 해야만 한다.
  • 조정성, 확장성, 생산성, 단순성이 높다.
    • 위에 있던 이유로 높은 것인데, 이것은 장점일 수도 있지만 제일 큰 문제를 일으킬 수 있다.
  • 모든 것이 미들웨어라고 이야기를 할만큼 활용성이 높다.
    • 하지만 작업을 할 때엔 존재하지 않아서 직접 구현을 하거나 찾아와야한다.
  • JS 기반이기에 데코레이터를 사용할 수 없습니다.
    • TS로 라이브러리를 깔아서 사용할 수는 있지만, 불안정성이 존재한다
  • 프레임워크가 IoC를 지원하지 않아 라이브러리를 설치해야한다
    • DI 또한 없어서 라이브러리를 설치해줘야 한다
  • async await를 공식적으로는 지원하지 않는다
    • 5.0버전에 지원한다고 했으나 업데이트가 느리다
    • 라이브러리를 다운받는 것으로 사용할 수 있긴 한데...
  • 업데이트를 멈췄다
    • 6개월 전 오류 해결을 위해 업데이트를 하고 간혹 하고 있는데, 2년간은 업데이트가 없었다.

그렇다면 이제 Nest.js의 장점

편리성

  • 기존의 Express는 Micro Framework이기에 설치할 라이브러리가 너무 많을 뿐 아니라 폴더를 하나씩 생성해야 한다는 점이 비효율적이었습니다
    • cli를 전역으로 설치 후 명령어를 통해 기본적인 파일과 폴더들을 설치해줍니다
  • 개발하는 데 자주 사용하는 기능들이 내장되어있습니다
    • IoC나 유효성 검사를 해주는 기능들은 기존에 Express에서는 Joi, typeDI를 통해 설치를 해야 했지만 NestJS는 이러한 기능들이 데코레이터를 통해 사용할 수 있습니다.

안정성

  • NestJS는 물론 자바스크립트 js에서도 작동이 가능하지만, nestJS 개발 당시 타입스크립트를 고려하여 제작되었기 때문에 타입스크립트를 적극적으로 지원합니다.
  • 타입스크립트를 지원한다는 것은, 타입스크립트의 장점 또한 갖고 있다는 것으로 보면 됩니다.
  • 타입스크립트는 타입을 지정하여 개발자 또는 시스템이 코드를 읽고 디버깅 하는 데에 자바스크립트의 몇십배는 더 편하게 만들어 준다고 생각하는데, 이를 서버 개발 시에도 적극 활용하여 발생 할 수 있는 이슈를 미연에 방지할 수 있습니다.

구조화

  • 재사용성이 좋은 모듈식 아키텍처가 정의되어있습니다
    • 기본 구조 자체가 통일화되어있기 때문에 구조 파악이 명확하고 간편하다.
    • 테스트 가능성이 높습니다
    • 느슨한 결합을 통해 유지보수가 쉬운 애플리케이션을 만들 수 있습니다
    • 빠른 개발이 가능하지만 자유도가 다소 떨어집니다

확장성

  • GraphQL, Mongoose, TypeORM, Elasticsearch같은 라이브러리가 자체적으로 지원합니다.
    • Nest 자체에 붙어있는 것이라 호환성 문제가 없다는 장점이 있습니다

캡슐화

  • 객체지향프로그래밍 OOP의 특성 중 하나인 캡슐화를 쉽게 할 수 있습니다
  • NestJS는 비슷한 기능을 하는 컨트롤러, 서비스 등을 묶어 module 파일 내에서 모두 관리합니다.
  • 아키텍쳐 자체가 모듈 별로 감싸진 형태로 작성되어 있기 때문에 nestJS 에서 지원하는 테스트를 직접 실행하며 안정성있게 작업을 할 수 있습니다
  • 이처럼 간단하게 모두 분기시켜서 관리할 수 있다는 특징이 있습니다.

전망

  • 오픈 소스이며 매년 사용자가 늘고 있지만 정보는 적은 편입니다
    • 공식문서가 매우 잘되어있고 번역이 빠르게 진행되고 있습니다

Nest.js의 Controller, Provider 그리고 module

  • nestjs는 기본적으로 프로바이더와 컨트롤러를 모듈에 합치고 모듈들을 최종적으로 앱모듈에 합치는 컨셉을 채택하고 있습니다
  • 인스턴스를 새롭게 생성하지 않고 모듈을 통해 의존성을 주입하는 싱글톤 패턴을 권장하고 있습니다
  • 이 링크에 nest.js의 기본적인 컨셉이 간단하게 설명되어있습니다.
  • 그럼 아래부터는 이 프로바이더,컨트롤러,모듈이 무엇이고 어떻게 사용했는지 직접 경험한바를 토대로 포스팅을 해보겠습니다

Controller

  • 컨트롤러는 외부의 요청을 처리하는 모듈을 의미합니다
  • 클라이언트가 보내는 요청을 처리하고 응답을 반환하는 역할입니다
  • 컨트롤러의 목적은 특정 요청을 수신하는 것으로, 어떤 컨트롤러가 해당 요청을 처리할지 조정합니다
  • 따라서 컨트롤러는 하나 이상의 경로가 있습니다 @Controller 데코레이터를 통해 다음과 같이 경로를 설정합니다
@Controller({ path: 'feed', version: 'v1' })
export class FeedController {
  constructor(private readonly feedService: FeedService) {}
}
  • path를 통해 경로를 설정해주고 버전을 적어줄 수 있습니다(이 버전은 근데 별 기능을 안하는 거 같던데...무슨 기능을 하는지 모르겠네요)
  • 이렇게 하면 @Get,@Post와 같은 핸들러의 인수에 따라
    • /feed/1
    • /feed/leo3179
  • 이렇게 api url을 설정해줄 수 있습니다
  • 라우팅을 하는 모듈이라고 할 수 있습니다
  • 라우팅이란?
    • 네트워크에서는 어떠한 패킷을 원하는 곳으로 보내는 행위

Provider

  • nestjs의 모든 데이터 처리 및 비즈니스 로직을 담당합니다

  • 계층형 구조(레이어드 아키텍쳐)라는 기법을 사용합니다

  • 간단히 말해 작업을 종류별로 나누어서 역량을 집중하게 하는것이라고 보면 됩니다

    • 보통 3계층 구조를 사용하는데 다음과 같습니다
    • 3계층 구조
      • Presentation Tier: 사용자 인터페이스 혹은 외부와의 통신을 담당합니다.
      • Application Tier: Logic Tier라고 하기도 하고 Middle Tier라고 하기도 합니다. 주로 비즈니스 로직을 여기서 구현을 하며, Presentation Tier와 Data Tier사이를 연결해줍니다.
      • Data Tier: 데이터베이스에 데이터를 읽고 쓰는 역할을 담당합니다.
    • 이는 응집도는 높이고 결합도를 낮춰서 유지-보수를 쉽게 만드는 소프트웨어 설계입니다.
  • 제어 역전(IoC)과 의존성 주입(DI)

  • 제어 역전(Inversion of Control)

    • 나 대신 프레임워크가 제어한다는 뜻입니다
      • 우리는 클래스 혹은 인터페이스과 같은 추상화한 붕어빵틀을 이용해 붕어빵을 찍어냅니다
      • 그리고 팥을 넣을지 슈크림을 넣을지도 정해줘야 되죠
      • 그럴 때마다 다음과 같이 new 인스터스화를 해야 합니다
        				const sweetBeanBoong = new boong(new sweetBean())
        				const creamBoong = new boong(new cream())
      • 클래스 계층 구조가 복잡할 수록 팥을 넣을지 크림을 넣을지를 다음과 같이 새로운 인스턴스를 만들어주는 것은 좋지 않기 때문에 이 경우 제어 역전을 사용하는 것입니다
      • 제어권을 프레임워크에 넘겨주면서 프레임워크는 코드의 소비자가 될 수 있습니다.
      • 이를 통해 프레임워크는 내가 짠 코드가 필요할 때 알아서 이를 실행시키게 됩니다
      • 이를 제어 역전이라 합니다
  • 의존성 주입 (Dependency Injection)

    • 위에서 말한 것처럼 필요한 클래스를 프레임워크가 대신 관리해주는 개념이라고 할 수 있습니다
    • DI는 위에서 말한 IoC의 구현체 중 하나라고 생각하면 됩니다
    • nest.js는 DI를 통해 IoC를 구현한 프레임워크라고 보면 됩니다
  • nestjs 프로바이더의 주요 개념은 의존성을 주입할 수 있다는 것입니다

  • 이 뜻은 서로가 다양한 관계를 만들 수 있다는 것을 의미하며 이 연결 기능을 nest 시스템이 담당해준다고 할 수 있습니다

  • 컨트롤러가 http 요청을 처리한다면 이보다 복잡한 일은 프로바이더에게 위임하는 것입니다

  • 위의 sweetBean()와 같이 주입당하는 객체를 바로 프로바이더라고 할 수 있습니다

  • 프로바이더는 역할에 따라 그 이름이 다음과 같이 달라집니다

    • 사용자 인증: Guards
    • 클라이언트가 보내는 데이터 필터링: Pipes
    • 비즈니스 로직: Service or Handler
    • 예외 처리: Exception Filters
    • Provider 처리 과정 중 위에 해당하지 않는 무언가를 하고 싶을 때: Interceptor
    • 미들웨어: Express의 Middleware와 동일
  • 처리 과정은 아래 이미지와 같습니다

    1. 미들웨어
    2. 가드에서 요청이 해당 api에 접근 가능한 것인지 확인
    3. pipe에서 클라이언트에서 요청과 함께 보낸 데이터를 원하는 형태로 가공
    4. 핸들러 혹은 서비스에서 필요한 작업(비즈니스 로직)을 수행
    5. 이 과정에서 발생한 예외처리를 Exception filter가 담당

이렇게 과정이 나뉘게 됩니다

위와 같은 이름을 가진 provider들을 활용한 경험을 기록해보겠습니다

1. Guard 활용

  • SNS 프로젝트에서 사용한 Guard는 로그인이 되었는지 확인하는 인증 가드(AuthGuard)입니다
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

/*
  - jwt 인증이 유효한지 확인한다.
*/
@Injectable()
export class JwtAccessGuard extends AuthGuard('access') {}
  • 사용자 인증이 필요한 경우 클라이언트에서 로그인하면서 발급받은 jwt-access-token을 요청 헤더에 넣어 보내줍니다
  • 이를 위의 가드를 통해 아래 jwt-strategy를 실행하고, secret-key를 통해 복호화하여 context에 jwt-token의 페이로드값을 붙여서 보내주는 방식입니다
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';

@Injectable()
export class JwtAccessStrategy extends PassportStrategy(Strategy, 'access') {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      // 헤더에서 액세스 토큰을 가져와 검증
      secretOrKey: process.env.JWT_ACCESS_KEY,
      passReqToCallback: true,
    });
  }

  async validate(req, payload) {
    const accessToken = await req.headers.authorization.split(' ')[1];

    if (!accessToken) throw new UnauthorizedException('액세스 토큰이 없습니다');

    return { email: payload.email, createdAt: payload.createdAt };
  }
}
  • 여기서 사용한 passport 모듈은 인기있는 node.js 인증 라이브러리입니다.
  • 전략 패턴을 활용한 인증 미들웨어 라이브러리로 여러가지 전략들을 기반으로 인증을 할 수 있게 해주는 라이브러리입니다
  • 사용자를 인증하고, 인증 상태를 관리하고 token을 통해 복호화된 사용자의 정보를 route-handler를 통해 요청 객체에 첨부를 해주는 역할을 하고 있습니다

전략패턴이란?

  • 전략 패턴은 객체의 행위를 바꾸고 싶은 경우 이를 '직접' 수정하지 않고 '전략'이라고 부르는 '캡슐화한 알고리즘'을 컨텍스트 내에서 바꿔주는 패턴을 말합니다
  • 위에서는 'access'라는 액세스 토큰 인증을 위한 전략 패턴을 사용했습니다. 여기서 이름을 다르게 하여 아래와 같이 쿠키의 리프레시 토큰을 인증하는 'refresh'라는 전략 패턴을 만들수도 있습니다
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-jwt';

@Injectable()
export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'refresh') {
  constructor() {
    super({
      jwtFromRequest: (req) => req.headers.cookie.replace('refreshToken=', ''),
      // 쿠키에서 리프레시 토큰을 가져와 검증
      secretOrKey: process.env.JWT_REFRESH_KEY,
      passReqToCallback: true,
    });
  }

  async validate(req, payload) {
    const refreshToken = await req.headers.cookie.replace('refreshToken=', '');
    if (!refreshToken)
      throw new UnauthorizedException('리프레시 토큰이 없습니다');

    return { email: payload.email, createdAt: payload.createdAt };
  }
}
  • 이렇게 JwtAccessGuard 혹은 JwtRefreshGuard를 만들었다면 이를 모듈의 provider에 넣어 해당 모듈에 전역으로 사용해줘도 됩니다
  • 아래에서는 피드와 관련된 전체 api에 가드를 적용하는 모습입니다

@Module({
  imports: [TypeOrmModule.forFeature([Feed, FeedLike, User]), CqrsModule],
  controllers: [FeedController],
  providers: [FeedService, UserService, FetchFeedQueryHandler, JwtAccessGuard],
})
export class FeedModule {}
  • 하지만 인증이 필요하지 않은 api도 있고 다른 가드가 필요한 경우가 있기 때문에 저는 다음과 같이 @UseGuards 데코레이터를 활용해 가드를 적용했습니다
  • 데코레이터에 관해서는 추후에 설명을 다시 해보겠습니다
  // 피드 생성 API
  //@UseGuards로 JwtAccessGuard 사용한 사례
  @Post()
  @UseGuards(JwtAccessGuard)
  createFeed(
    @CurrentUser() currentUser: ICurrentUser,
    @Body(ValidationPipe) createFeedInput: CreateFeedInput,
  ): Promise<Feed> {
    return this.feedService.create({ currentUser, createFeedInput });
  }

  // 액세스 토큰 복구 API
  // @UseGuards로 JwtRefreshGuard 사용한 사례
  @Post('restoreAccessToken')
  @UseGuards(JwtRefreshGuard)
  restoreAccessToken(
    @CurrentUser() currentUser: ICurrentUser, //
  ): Promise<string> {
    return this.authService.getAccessToken({ user: currentUser });
  }
}

2. Pipe 활용

  • 가드가 끝났으면 다음 차례는 파이프입니다

  • 파이프는 클라이언트로부터 온 데이터를 처리해주는 역할입니다

  • 문제가 생기면 Error를 뱉고 통과한다면 처리된 데이터가 비즈니스 로직으로 들어가게 됩니다

    파이프는 data-transformation과 data-validation을 위해 사용됩니다.

    • Data Transformation
      입력 데이터를 원하는 형식으로 변환하는 것을 말한다. 가령 문자열에서 정수로 바꾸는 것을 의미한다.
    • Data Validation
      유효성 체크로서, 입력 데이터를 평가하고 유효한 경우 변경되지 않은 상태로 전달된다. 그렇지 않으면 데이터가 올바르지 않을 때 예외를 발생시킨다.
  • 제가 사용한 기능은 이중에서 data-validation, 즉 올바르지 않은 데이터가 들어왓을 때 예외처리를 해주는 기능을 적용했습니다

  • 다만 provider에서 사용하진 않고 다음과 같이 main.ts에 useGlobalPipes()를 활용해 global로 적용했습니다

  app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
      enableDebugMessages: true,
      exceptionFactory(errors) {
        const message = Object.values(errors[0].constraints);
        throw new BadRequestException(message[0]);
        // 예외처리 : 이렇게 해두면 어떤 인풋의 타입에러가 발생했는지를 
        // 에러 메시지를 통해 보여줍니다
      },
    }),
  );
  • 사용한 파이프는 ValidationPipe로 빌트인 파이프를 사용했습니다

  • 이 파이프를 사용하기 위해선 class-validator, class-transformer 모듈을 설치해야 합니다

  • 이렇게 글로벌로 적용한 파이프가 작동되기 위해서는 해당 dto에 다음과 같이 class-validator의 모듈에서 임포트한 데코레이터를 추가해줘야합니다

export class CreateFeedInput {
  @IsString() // class-validator 데코레이터
  @IsNotEmpty()
  @ApiProperty({
    description: '게시글 제목', 
    example: '서울 맛집 추천!!',
  })
  title: string;

  @IsString()
  @IsNotEmpty()
  @ApiProperty({
    description: '게시글 내용',
    example: '종로구 부대찌개 맛집 추천드려요!',
  })
  content: string;

  @IsString()
  @ApiProperty({
    description: '게시글 해시태그',
    example: '#종로구,#부대찌개,#주말,#맛집',
  })
  hashTags: string;
}
  • 만약 위에 타이틀이 숫자타입으로 요청이 들어왔다면 @IsString 데코레이터가 원하는 문자열 타입이 아니기 때문에 타입에러를 리턴합니다.
  • provider를 사용하진 않았지만 이렇게 글로벌로 해두면 편리하긴 합니다
  • 상황에 따라 파이프를 적용해야 할경우 글로벌파이프를 해제하고 provider에 적용하면 될 것 같습니다
  • data-transform을 사용해보진 않았는데, 유용할 거 같아 필요할 경우 사용해보고 싶습니다

3. Service 혹은 Handler

  • 서비스와 핸들러는 비즈니스 로직을 수행하는 프로바이더입니다

  • 서비스

    • 서비스는 Injectable 데코레이터를 이용해 다른 컴포넌트에서 해당 비즈니스 로직을 사용할 수 있게 만듭니다
      @Injectable
      export class UserService {
      constructor(@InjectRepository(User) private readonly userRepository: Repository<User>) {}
      
      	async fetch({ email }) {
      	 const user = await this.userRepository.findOne({ where: { email } });
      	 if (!user) throw new NotFoundException('유저 정보가 존재하지 않습니다');
      	 return user;
      	}
      }
      		```
      
    • 이렇게 비즈니스 로직을 위한 함수들만 모아놓은 프로바이더이며 이를 컨트롤러에서 임포트한 후 다음과 같이 constructor에 넣어서 사용합니다
      @Controller('user')
      export class UserController {
      constructor(private readonly userService: UserService) {}
      }
      	```
  • 핸들러

    • @Get , @Post, @Delete 등과 같은 데코레이터로 장식된 컨트롤러 클래스 내의 단순한 메서드입니다.
    @Get() // 핸들러
    fetchUser(@Query('email') email: string): Promise<User> {
      return this.userService.fetch({ email });
    }
    
    @Get('list') // 핸들러
    fetchUsers(): Promise<User[]> {
      return this.userService.fetchAll();
    }
    • 다음과 같이 컨트롤러 데코레이터 클래스안에 함수에 데코레이터 문법으로 장식한 함수를 핸들러라고 합니다.

4. Exception filter

  • nest.js는 프레임워크 내에 예외 레이어를 두고 있습니다
  • 이 예외 레이어는 제대로 처리하지 못한 예외를 처리해주는 역할을 합니다
  • 이 기본 전역 필터 외에 직접 커스텀하여 예외 필터 레이어를 두어 원하는대로 예외를 다룰 수 있습니다.
  • 저는 아래와 같이 exception filter를 커스텀해서 만들어 main.ts에 전역으로 설정해두었습니다.
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();

    const status =
      exception instanceof HttpException //
        ? Number(exception.getStatus())
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const message = exception.message;

    console.log('==========');
    console.log('에러 발생!');
    console.log('에러코드: ', status);
    console.log('에러내용: ', message);
    console.log('==========');
    response.status(status).json({
      statusCode: status,
      message,
      timestamp: new Date(),
    });
  }
}
 app.useGlobalFilters(new HttpExceptionFilter());
  • 이렇게 만들어둔 필터는 예외를 전역으로 처리하며 커스터마이징 한 모양으로 콘솔로그를 전송해줍니다
  • nest.js는 기본적으로 여러가지 exception 클래스를 제공하는데 이를 이용해 다음과 같이 예외를 발생 시켰습니다
if (!feed) throw new NotFoundException(ErrorType.feed.notFound.msg);
  • 이렇게 글로벌로 설정해두면 간단하긴 하지만, 모듈별로 보다 세밀하게 예외처리를 할 필요가 있을 수도 있습니다
  • 위와같이 @Catch 데코레이터를 이용한 상속 클래스를 만들어 해당 모듈의 프로바이더로 설정해주면 됩니다

Module

  • nest.js가 애플리케이션 구조를 만들 때 사용할 수 있는 메타 데이터를 제공해주는 역할을 합니다
  • @Module() 데코레이터는 아래 속성을 가지는 객체가 필요합니다. 이 객체는 모듈을 구성하는데 필요한 정보를 가지고 있습니다.
    • providers(프로바이더): Nest 인젝터(Injector: 의존성을 주입하는 Nest 내부 모듈)가 인스턴스화시키고 적어도 이 모듈 안에서 공유하는 프로바이더.
    • controllers(컨트롤러): 이 모듈안에서 정의된, 인스턴스화 되어야하는 컨트롤러의 집합
    • imports: 해당 모듈에서 필요한 모듈의 집합. 여기에 들어가는 모듈은 프로바이더를 노출하는 모듈입니다.
    • exports: 해당 모듈에서 제공하는 프로바이더의 부분집합이며, 이 모듈을 가져오는 다른 모듈에서 사용할 수 있도록 노출할 프로바이더
  • Nest 모듈 시스템에는 동적 모듈 이라 불리는 강력한 기능이 포함되어 있습니다. 이 기능을 사용하면 프로바이더를 동적으로 등록하고 구성할 수 있는 커스터마이징 모듈을 쉽게 만들 수 있습니다.
  • 이 부분은 따로 공부를 더 해봐야 할 거 같아 따로 포스팅 하기로 합니다

정리

  • Nest.js란
    • node.js에 있어서 아키텍쳐의 부재를 해결하기 위해 만들어진 프레임워크
  • Express.js의 단점
    • node.js의 express.js는 쉽고 성능이 뛰어나지만 구조화되지 않은 마이크로 프레임워크라 설치하고 설정할게 많으며 구조화를 위한커뮤니케이션 비용이 발생한다
  • Nest.js의 장점
    • IoC 기능 등의 개발 기능들이 내장되어있다
    • 타입스크립트를 사용하기 때문에 안정적이다
    • 재사용성이 좋은 모듈식 아키텍처가 정의되어있다
    • GraphqQL, TypeORM 등의 외부호환 라이브러리를 자체적으로 지원한다

provider를 정리하면서 IoC,DI등의 개념을 정리하고 provider들을 다시 복습하니 코드를 짜면서 찝찝했던 부분이 많이 해소가 되는 것 같습니다
사실 모듈을 사용하면서도 provider들을 명확하게 알지 못했는데, provider를 제대로 복습하고 리뷰한 점이 꽤나 도움이 된 것 같습니다

nest.js가 단순히 라이브러리 하나가 아닌 엔터프라이즈급의 서버개발 프레임워크고 포스팅할게 너무 많기 때문에 리뷰를 앞으로도 많이 해야 할 것 같습니다 추가로 포스팅해야 할 개념들을 적어보고 마무리 지을게요

  • 동적 모듈
  • nestjs의 디자인 패턴
    • 싱글톤 패턴
    • CQRS 패턴
    • 전략 패턴
    • 등등...

Reference

https://www.wisewiredbooks.com/nestjs/
https://nemne.tistory.com/m/26
https://velog.io/@chappi/Nestjs%EB%A5%BC-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90-6%EC%9D%BC%EC%B0%A8-Pipe-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC-John-ahn%EB%8B%98-%EA%B0%95%EC%9D%98
https://velog.io/@kimdlzp/%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90
https://velog.io/@yukina1418?tag=NestJS

profile
개발 공부중

0개의 댓글