2023. 6. 14. 16:56ㆍ개발 문서 번역/NestJS
이 포스팅은 「NestJS 기초실무 가이드 : 강력하고 쉬운 Node.js 웹 프레임워크로 웹사이트 만들기」
(서명: NestJS 基礎必學實務指南:使用強大且易擴展的 Node.js 框架打造網頁應用程式)
책이 출간되었습니다.
예전 포스팅에서 Provider를 주입하는 방법은 constructor의 파라미터에 해당 형식을 지정하거나
@Inject 데코레이터를 사용하여 해당 인스턴스를 얻어올 수 있는 방법이 있다고 설명드렸었습니다.
아래는 app.controller.ts의 예제입니다.
constructor에 파라미터와 AppService라고 형식을 지정해주면 AppService의 인스턴스를 가져올 수 있습니다.
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
Nest는 또 다른 방법으로도 내부 Provider의 인스턴스를 가져올 수 있는데요.
이를 모듈 참조(Module Reference, 模組參照) 라고 부릅니다.
모듈 참조란 무엇인가요?
모듈 참조란 ModuleRef의 class를 말합니다.
내부 Provider에 접근하여 해당 모듈의 Provider를 관리하는 매니저라고 볼 수 있겠습니다.
Module Reference 사용하기
Provider 주입 방법과 동일하며 constructor에 주입하면 됩니다.
아래는 app.controller.ts의 예제입니다.
import { Controller } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
@Controller()
export class AppController {
constructor(
private readonly moduleRef: ModuleRef
) {}
}
인스턴스 가져오기
ModuleRef을 주입하고 나서 get메서드로 앞전의 Module 아래 있는 어떠한 컴포넌트도 가져올 수 있습니다.
(ex: Controller, Service, Guard) 등..
주의: 이 방법은 기본 범위(https://wth2052.tistory.com/470) 이외의 구성에서는 사용할 수 없습니다.
app.controller.ts의 예제입니다. ModuleRef를 통해 AppService의 인스턴스를 가져오고 있습니다.
import { Controller, Get } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { AppService } from './app.service';
@Controller()
export class AppController {
private readonly appService: AppService;
constructor(
private readonly moduleRef: ModuleRef
) {
this.appService = this.moduleRef.get(AppService);
}
@Get()
getHello() {
return this.appService.getHello();
}
}
http://127.0.0.1:3000에 접속해보면 해당 방법으로도 정상적으로 인스턴스를 가져올 수 있음을 알 수 있습니다.
전역 인스턴스 가져오기
전역 인스턴스를 가져오고자 한다면 strict를 false로 설정해야 전역 범위로 설정된 인스턴스를 가져올 수 있습니다.
먼저 StorageModule과 StorageService를 생성한 후 Module을 전역으로 설정해보겠습니다.
$ nest generate module common/storage
$ nest generate service common/storage
storage.module.ts를 다음과 같이 수정해보겠습니다.
import { Injectable } from '@nestjs/common';
@Injectable()
export class StorageService {
private list: any[] = [];
public addData(data: any): void {
this.list.push(data);
}
public getList(): any[] {
return this.list;
}
}
이어서 app.controller.ts를 수정하여 ModuleRef를 통해 전역 인스턴스 StorageService를 가져오도록 설정하겠습니다.
import { Controller, Get } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { StorageService } from './common/storage/storage.service';
@Controller()
export class AppController {
private readonly storageService: StorageService;
constructor(
private readonly moduleRef: ModuleRef
) {
this.storageService = this.moduleRef.get(StorageService, { strict: false });
this.storageService.addData({ name: 'WOO' });
}
@Get()
getHello() {
return this.storageService.getList();
}
}
서버를 가동한 후 http://127.0.0.1:3000에 접속하면 아래와 같은 결과를 확인할 수 있습니다.
기본 범위가 아닌 Provider 처리하기
기본 범위 이외의 구성에서는 get을 통해 인스턴스를 가져올 수 없습니다.
그렇다면 어떻게 처리해야 할까요? 이때는 resolve를 통해 해결하곤 합니다.
resolve는 자체 의존성 주입 컨테이너 서브트리 (DI Container sub-tree, 依賴注入容器子樹)에서 인스턴스를 반환하며
각 서브트리는 고유한 컨텍스트 식별자(Context Identifier, 識別碼) 를 가지므로 매번 resolve 할 때마다 각기 다른 인스턴스가 반환됩니다.
간단한 실험을 하나 해보겠습니다. 먼저 AppService를 요청 범위로 변경하겠습니다.
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
이어서 AppController에 resolve를 두번 사용하여 해당 인스턴스가 같은 인스턴스인지 비교해보겠습니다.
서버를 가동하고 나면 터미널에는 false라는 결과가 출력될것입니다.
import { Controller, OnModuleInit } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { AppService } from './app.service';
@Controller()
export class AppController implements OnModuleInit {
constructor(
private readonly moduleRef: ModuleRef
) {}
async onModuleInit() {
const [instance1, instance2] = await Promise.all([
this.moduleRef.resolve(AppService),
this.moduleRef.resolve(AppService)
]);
console.log(instance1 === instance2); // false
}
}
수동으로 컨텍스트 식별자 구성하기
다수의 resolve 호출 상황에서 같은 인스턴스를 반환하고 싶다면 컨텍스트 식별자를 지정해주어 같은 서브트리를 사용하게 하면 됩니다. 식별자를 지정하기 전에 먼저 ContextIdFactory 클래스의 create 메서드를 사용하여 식별자를 생성할 수 있습니다. 아래의 AppController를 예제로 들어보겠습니다.
resolve 전에 식별자가 생성된 후 resolve에 전달됩니다.
Nest 서버를 키고난 뒤에 터미널에는 true라는 결과가 출력될것입니다.
서브트리 공유
요청 범위 내에서 ContextIdFactory의 getByRequest를 통해 요청 객체에 식별자를 만들 수 있는데
이 방법으로 서브트리를 공유하는 효과를 기대할 수 있습니다.
간단한 실험을 해보겠습니다. AppController안에 REQUEST를 주입하고 getRequest로 식별자를 얻어온 뒤
해당 식별자로 resolve를 2번 실행하여 생성된 인스턴스와 비교를 진행해보겠습니다.
import { Controller, Get, Inject } from '@nestjs/common';
import { ContextIdFactory, ModuleRef, REQUEST } from '@nestjs/core';
import { Request } from 'express';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(
private readonly moduleRef: ModuleRef,
@Inject(REQUEST) private readonly request: Request
) {}
@Get()
async getTruth() {
const identifier = ContextIdFactory.getByRequest(this.request);
const [instance1, instance2] = await Promise.all([
this.moduleRef.resolve(AppService, identifier),
this.moduleRef.resolve(AppService, identifier)
]);
return instance1 === instance2;
}
}
127.0.0.1:3000 으로 접속해보면 true 라는 값이 반환되었음을 확인할 수 있습니다.
마치며
오늘 배운 내용에 대해 간단히 정리해보았습니다.
1. ModuleRef는 내부 Provider에 접근할 수 있습니다.
2. ModuleRef의 기본값은 해당 모듈 아래에 있는 인스턴스에만 접근할 수 있지만 strict 옵션을 사용하면 전역 인스턴스에도 접근이 가능합니다.
3. 기본 범위가 아닐때는 resolve를 통해 인스턴스를 얻어올 수 있습니다.
4. 기본값일때는 resolve에서 다른 인스턴스를 반환합니다. 식별자를 통해 같은 인스턴스를 반환하게 할 수 있습니다.
5. ModuleRef로 식별자를 고정할 수 있습니다.
6. ContextIdFactory를 통해 식별자와 요청 객체 안의 식별자를 생성할 수 있습니다.
고급 기능에 대한 내용은 여기까지입니다.
다음 장에서는 다양한 기능에 대해 다룰 예정입니다. 기대해주세요!
'개발 문서 번역 > NestJS' 카테고리의 다른 글
NestJS 帶你飛! 시리즈 번역 21# HTTP Module (0) | 2023.06.18 |
---|---|
NestJS 帶你飛! 시리즈 번역 20# File Upload (0) | 2023.06.16 |
NestJS 帶你飛! 시리즈 번역 18# Lifecycle Hooks (0) | 2023.06.13 |
NestJS 帶你飛! 시리즈 번역 17# Injection Scopes (0) | 2023.06.12 |
NestJS 帶你飛! 시리즈 번역 16# Configuration (0) | 2023.06.10 |