들어가면서
의존성 주입을 공부하면서 @nestjs/core의 패키지를 뜯어보게 되었다.
이 과정에서 @nestjs/core/injector/container.d.ts 파일을 보게 되었는데 해당 파일의 끝부분의 코드가 다음과 같았다.
import { DynamicModule, Provider } from '@nestjs/common';
import { EnhancerSubtype } from '@nestjs/common/constants';
import { Injectable, Type } from '@nestjs/common/interfaces';
import { ApplicationConfig } from '../application-config';
import { SerializedGraph } from '../inspector/serialized-graph';
import { ModuleCompiler } from './compiler';
import { ContextId } from './instance-wrapper';
import { Module } from './module';
import { ModuleTokenFactory } from './module-token-factory';
import { ModulesContainer } from './modules-container';
type ModuleMetatype = Type<any> | DynamicModule | Promise<DynamicModule>;
type ModuleScope = Type<any>[];
export declare class NestContainer {
private readonly _applicationConfig;
private readonly globalModules;
private readonly moduleTokenFactory;
private readonly moduleCompiler;
private readonly modules;
private readonly dynamicModulesMetadata;
private readonly internalProvidersStorage;
private readonly _serializedGraph;
private internalCoreModule;
constructor(_applicationConfig?: ApplicationConfig);
get serializedGraph(): SerializedGraph;
get applicationConfig(): ApplicationConfig | undefined;
setHttpAdapter(httpAdapter: any): void;
getHttpAdapterRef(): import("..").AbstractHttpAdapter<any, any, any>;
getHttpAdapterHostRef(): import("..").HttpAdapterHost<import("..").AbstractHttpAdapter<any, any, any>>;
addModule(metatype: ModuleMetatype, scope: ModuleScope): Promise<{
moduleRef: Module;
inserted: boolean;
} | undefined>;
replaceModule(metatypeToReplace: ModuleMetatype, newMetatype: ModuleMetatype, scope: ModuleScope): Promise<{
moduleRef: Module;
inserted: boolean;
} | undefined>;
private setModule;
addDynamicMetadata(token: string, dynamicModuleMetadata: Partial<DynamicModule>, scope: Type<any>[]): Promise<void>;
addDynamicModules(modules: any[], scope: Type<any>[]): Promise<void>;
isGlobalModule(metatype: Type<any>, dynamicMetadata?: Partial<DynamicModule>): boolean;
addGlobalModule(module: Module): void;
getModules(): ModulesContainer;
getModuleCompiler(): ModuleCompiler;
getModuleByKey(moduleKey: string): Module;
getInternalCoreModuleRef(): Module | undefined;
addImport(relatedModule: Type<any> | DynamicModule, token: string): Promise<void>;
addProvider(provider: Provider, token: string, enhancerSubtype?: EnhancerSubtype): string | symbol | Function;
addInjectable(injectable: Provider, token: string, enhancerSubtype: EnhancerSubtype, host?: Type<Injectable>): string | symbol | Function | import("./instance-wrapper").InstanceWrapper<unknown>;
addExportedProvider(provider: Type<any>, token: string): void;
addController(controller: Type<any>, token: string): void;
clear(): void;
replace(toReplace: any, options: any & {
scope: any[] | null;
}): void;
bindGlobalScope(): void;
bindGlobalsToImports(moduleRef: Module): void;
bindGlobalModuleToModule(target: Module, globalModule: Module): void;
getDynamicMetadataByToken(token: string): Partial<DynamicModule>;
getDynamicMetadataByToken<K extends Exclude<keyof DynamicModule, 'global' | 'module'>>(token: string, metadataKey: K): DynamicModule[K];
registerCoreModuleRef(moduleRef: Module): void;
getModuleTokenFactory(): ModuleTokenFactory;
registerRequestProvider<T = any>(request: T, contextId: ContextId): void;
private shouldInitOnPreview;
}
export {};
파일 끝의 export {};의 의미
Typescript의 모듈 시스템과 관련된 문법이며 역할은 다음과 같다.
1. 모듈로 간주되도록 강제
Typescript에서 파일이 모듈로 간주되려면 적어도 하나의 export 또는 import 문이 있어야 한다.
export {};는 파일을 모듈로 간주하도록 강제하기 위해 사용되는데 이 문법 자체는 아무것도 내보내지는 않지만, 파일이 전역 스코프에서 격리되도록 한다.
2. 전역 스코프 독립성 보장
모듈 시스템에서는 파일 내부의 선언(예: 함수, 변수, 클래스 등)이 전역 스코프를 영향주지 않는다.
export {};를 사용하면 해당 파일이 전역 스코프와 독립된 모듈 스코프로 처리된다.
export {};를 사용하지 않는다면...
1. 전역 스코프 문제
Typescript에서 파일이 모듈로 간주되지 않으면, 해당 파일의 변수, 함수, 클래스 등이 전역 스코프에 노출된다.
이로 인해 다른 파일의 코드와 충돌하거나, 의도치 않게 값을 덮어쓸 위험이 생길 수 있게 된다.
2. --isolatedModules 플래그로 인한 오류
Typescript 프로젝트 설정에서 tsconfig.json에 --isolatedModules 옵션이 활성화 되어 있으면, 파일이 모듈로 간주되지 않을 경우 컴파일러가 오류를 발생시킨다.
3. export {}; 의 대안
export {};를 사용하지 않더라도, 파일을 모듈로 간주되게 하려면 파일 내에 import나 export 문을 추가하면 된다.
import { Something } from './another-file';
또는
export const value = 42;
이런 코드가 있다면 파일은 자동으로 모듈로 간주되므로 export {};를 명시적으로 추가할 필요가 없다.
※ 더 나아가기
Top-Level await 란?
Top-Level await는 최상위 코드에서 await를 직접 사용할 수 있도록 허용하는 기능을 말한다.
이전에는 await를 사용하려면 반드시 async 함수 내부에서 작성해야만 하였다.
// 기존 방식
async function fetchData() {
const response = await fetch('https://example.com');
console.log(await response.text());
}
fetchData();
이제는 Top-Level await를 사용하면 최상위 코드에서 await를 바로 사용할 수 있다.
// Top-Level await 사용
const response = await fetch('https://example.com');
console.log(await response.text());
하지만, 위 코드를 Typescript로 작성하면 컴파일 에러가 다음과 같이 발생한다.
그 이유는 Top-Level await 기능은 오직 모듈에서만 작동하기 때문이다.
모듈이 되기 위해서는 파일 내에 import나 export가 있거나 export {};가 있어야만 한다.
const response = await fetch("...");
export const greeting = await response.text();
또는
const response = await fetch("...");
const greeting = await response.text();
export {};
와 같이 작성하면 해결할 수 있다.
Reference
https://github.com/nestjs/nest/tree/master/packages/core
nest/packages/core at master · nestjs/nest
A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀 - nestjs/nest
github.com
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#top-level-await
Documentation - TypeScript 3.8
TypeScript 3.8 Release Notes
www.typescriptlang.org
'Nest.js' 카테고리의 다른 글
[NestJS] readonly란? | const와 비교, C++ 관점에서 비교 (0) | 2025.01.20 |
---|---|
[NestJS] export 키워드에 대해서 (1) | 2025.01.20 |
[NestJS] 데코레이터에 대해서 (2) | 2025.01.20 |
[NestJS] @Injectable() 데코레이터에 대하여 | IoC (0) | 2025.01.13 |