본문 바로가기
Node.js/Nest.js

NestJS Lazy-loading Modules

by Marades 2021. 8. 8.
반응형

간단 소개

  • Nest 버전 8에서 나온 최신 방법! 7미만이면 LazyModuleLoader를 불러오지도 못한다..
    • lazyModuleLoader를 찾을 수 없다면 NestCLI의 버전을 체크하고 업데이트 해주길 바란다!
  • 기존의 Module Load → Bootstrap과 동시에 모든 모듈 로드
  • Lazy-loading Modules 기법 → 필요한 모듈만 로드하여 부트스트랩 시간 단축

사용 방법

// cats.service.ts

@Injectable()
export class CatsService {
  constructor(private lazyModuleLoader: LazyModuleLoader) {}
}

Or

// main.ts

import { LazyModuleLoader, NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const lazyModuleLoader = app.get(LazyModuleLoader) // <- 이렇게도 호출 가능
  
  await app.listen(3000);
}
bootstrap();

위와 같은 방법으로 lazyModuleLoader 객체를 얻는다.

그 후 공식 문서에 따르면 아래와 같은 방법으로 쓴다.

const { LazyModule } = await import('./lazy.module');
const moduleRef = await this.lazyModuleLoader.load(() => LazyModule);

여기서 나오는 moduleRef 는 공식문서의 바로 전 장에 나오는 Module Reference 에서 나오는 인스턴스로서 module reference라는 말의 의미 그대로 get() 메소드를 통해 해당 모듈에 인스터스화된 provider 등을 불러올 수 있다.


예제

//dog.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class DogService {
    constructor() { }

    bark(): string {
        return 'wall wall'
    }
}
import { Injectable } from '@nestjs/common';
import { LazyModuleLoader } from '@nestjs/core';
import { DogService } from 'src/dog/dog.service';

@Injectable()
export class CatService {
    private service: DogService;
    constructor(private lazyModuleLoader: LazyModuleLoader) { }

    async dogBark(): Promise<any> {
        await this.changeToDog()
        return this.service.bark() // -> wall wall 출력
    }
    
    async changeToDog() {
        const { DogModule } = await import('../dog/dog.module')
        const moduleRef = await this.lazyModuleLoader.load(() => DogModule) // 이 순간 Load
        this.service = moduleRef.get(DogService)
    }
}

위 예제에서 dogModule은 app.module에 등록되어 있지 않아 첫 Bootstrap에 Load되지 않는다.

changeToDog 메소드 안의 lazyModuleLoader.load가 호출될 때 지정된 dogModule이 Load되어 캐싱된다.

 

즉 changeToDog 메소드가 다시호출되어 lazyModuleLoader.load가 다시 실행되더라도 dogModule은 이미 캐싱되어있기에 첫 호출보다 더 빠른 속도로 실행된다.

 

만약 dogService에 비동기적 요소가 들어가 있다면 공식문서에 나온 아래와 같은 방법을 사용하여 service를 불러오면 될 것 같다.

const { LazyService } = await import('./lazy.service');
const lazyService = moduleRef.get(LazyService);

주의사항

  • Nest의 Controller(GraphQL의 resolver) 는 Lazy-Load 불가능
    • app이 부트스트랩 된 후 route를 등록할 수 없다!
  • pub/sub 패턴을 사용할 경우에도 연결 설정 전에 미리 구독/수신 해야함
  • GraphQL은 모든 클래스 미리 로드해야함
    • 메타데이터를 기반으로 즉석에서 GraphQL 스키마를 자동으로 생성하기 때문

결론

  • Monolithic Architecture에서는 큰 의미를 가지지 않는다.
  • 필요한 모듈만 로드하기 때문에 Serverless 환경에서 Bootstrap의 병목을 줄여준다!

 

Reference

https://docs.nestjs.com/fundamentals/lazy-loading-modules

반응형