전역을 수정하는 모듈(Global-Modifying Module)

전역을 수정하는 모듈(Global-Modifying Module)

어떤 모듈이 그냥 가져오기만 해도 전역에 있는 기존 값들을 통째로 바꿔버리는 경우가 있어요. 이번 템플릿은 바로 그런 **전역을 수정하는 모듈(global-modifying module)**의 선언 파일을 어떻게 써야 하는지 알려드릴게요.

출처: TypeScript 공식문서

본문

전역을 수정하는 모듈이란

전역을 수정하는 모듈은 임포트되는 순간 전역 스코프(global scope)에 이미 존재하는 값을 바꾸는 모듈이에요. 예를 들어 어떤 라이브러리를 가져오기만 하면 String.prototype에 새 멤버가 추가되는 식이죠.

이 패턴은 실행 시점에 충돌이 일어날 가능성이 있어서 꽤 위험한 편이에요. 그래도 선언 파일을 작성할 수는 있답니다.

전역을 수정하는 모듈 알아보기

전역을 수정하는 모듈은 보통 문서만 봐도 쉽게 알아볼 수 있어요. 대체로 전역 플러그인(global plugin)과 비슷한데, 효과를 발동시키려면 require 호출이 필요하다는 점이 달라요.

문서에 이런 식으로 적혀 있는 걸 보게 될 거예요.

// 'require' call that doesn't use its return value
var unused = require("magic-string-time");
/* or */
require("magic-string-time");

var x = "hello, world";
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());

이건 그에 해당하는 선언 파일 템플릿이에요.

// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
// Project: [~THE PROJECT NAME~]
// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>

/*~ This is the global-modifying module template file. You should rename it to index.d.ts
*~ and place it in a folder with the same name as the module.
*~ For example, if you were writing a file for "super-greeter", this
*~ file should be 'super-greeter/index.d.ts'
*/

/*~ Note: If your global-modifying module is callable or constructable, you'll
*~ need to combine the patterns here with those in the module-class or module-function
*~ template files
*/

declare global {
  /*~ Here, declare things that go in the global namespace, or augment
   *~ existing declarations in the global namespace
   */
  interface String {
    fancyFormat(opts: StringFormatOptions): string;
  }
}

/*~ If your module exports types or values, write them as usual */
export interface StringFormatOptions {
  fancinessLevel: number;
}

/*~ For example, declaring a method on the module (in addition to its global side effects) */
export function doSomething(): void;

/*~ If your module exports nothing, you'll need this line. Otherwise, delete it */
export {};

핵심만 짚어볼게요. 전역에 추가하고 싶은 선언은 declare global { ... } 블록 안에 넣어요. 위 예시에서는 String 인터페이스를 declare global 안에서 열어 fancyFormat 메서드를 추가했죠. 이렇게 하면 기존 전역 타입을 그대로 늘리는(augment) 선언이 돼요.

그리고 모듈이 값이나 타입을 내보내는 경우에는 평소처럼 export로 작성하면 돼요. export interface StringFormatOptions, export function doSomething() 같은 게 그 예시예요.

마지막 줄의 export {};는 조금 특별한데요, 이 모듈이 아무것도 내보내지 않을 때 필요한 줄이에요. export가 하나라도 있으면 이 줄은 지워도 되고요. export가 전혀 없다면 이 파일이 모듈이 아니라 전역 스크립트로 오해받을 수 있으니, 굳이 내보낼 게 없어도 이 줄은 꼭 남겨두는 편이 안전해요.

callable/constructable 모듈이라면?

만약 이 전역 수정 모듈이 호출 가능(callable)하거나 생성 가능(constructable)한 형태라면, 여기 패턴만으로는 부족하고 모듈-클래스(module-class)나 모듈-함수(module-function) 템플릿의 패턴을 함께 조합해야 해요. 템플릿 주석에도 그대로 안내가 적혀 있죠.

더 알아보기