네임스페이스 사용법
namespace로 전역 코드를 계층화하고 여러 파일과 별칭으로 확장하며 현대 프로젝트에서 ES 모듈을 우선할 이유를 판단합니다.
타입스크립트는 초기 버전부터 코드 조직화를 위해 네임스페이스(Namespaces)를 제공해 왔습니다.
이 기능은 전역 스코프 오염을 줄이고 관련 코드를 묶는 데 유용했습니다.
다만 ES 모듈이 표준이 된 현재, 대부분의 현대 타입스크립트 프로젝트에서 네임스페이스 사용은 권장되지 않습니다.
그럼에도 불구하고 네임스페이스의 개념과 사용법을 이해하는 것은 중요합니다.
기존의 레거시 코드베이스를 다루거나, ES 모듈이 도입되기 전의 타입스크립트 코드를 분석할 때 마주칠 수 있기 때문입니다.
또한, 네임스페이스는 타입스크립트 내부적으로 타입 정의 파일(d.ts)에서 전역 객체나 외부 라이브러리의 구조를 모델링하는 데 여전히 사용되기도 합니다.
다음 그림은 선언 파일, 모듈 내부, 전역 스크립트 중 어느 문맥에 쓰인 네임스페이스인지부터 구분합니다.
네임스페이스의 선언과 구성
AMBIENT · MODULE LOCAL · LEGACY SCRIPT
namespace 문맥과 마이그레이션 판단
namespace를 발견하면 먼저 선언 문맥을 판별하고, legacy runtime namespace에만 global·state·load order inventory 뒤 ESM 전환을 적용한다.
- 어느 문맥인가?
NAMESPACE— 선언 파일 · module · script - declare namespace
AMBIENT— 기존 global/UMD shape - external module 내부
MODULE— local grouping 필요성 재검토 - runtime namespace
SCRIPT— global object를 생성 - exports · hidden state
INVENTORY— consumer · load order 고정 - ESM named exports
MIGRATE— regression test로 동등성
merged namespace의 non-exported member는 원래 block 밖에서 보이지 않는다. ambient 선언은 runtime 값을 만들지 않는다.
네임스페이스는 namespace 키워드를 사용하여 선언합니다.
네임스페이스 내부에 선언된 변수, 함수, 클래스 등은 기본적으로 해당 네임스페이스의 스코프 내에 캡슐화됩니다.
namespace Validators {
export interface StringValidator { // 네임스페이스 외부에서 사용하려면 export 필요
isValid(s: string): boolean;
}
export class ZipCodeValidator implements StringValidator {
isValid(s: string): boolean {
return s.length === 5 && /^\d+$/.test(s);
}
}
// 내부적으로만 사용되는 함수 (export 하지 않음)
function isLetter(char: string): boolean {
return /[a-zA-Z]/.test(char);
}
export class LettersOnlyValidator implements StringValidator {
isValid(s: string): boolean {
for (let i = 0; i < s.length; i++) {
if (!isLetter(s[i])) { // 네임스페이스 내부에서는 자유롭게 접근 가능
return false;
}
}
return true;
}
}
}위 예시에서 Validators 네임스페이스는 StringValidator 인터페이스, ZipCodeValidator 클래스, LettersOnlyValidator 클래스를 포함합니다.
isLetter는 export되지 않았으므로 이를 선언한 블록 내부에서만 접근할 수 있습니다. 같은 이름으로 병합된 다른 namespace 블록에는 노출되지 않습니다.
네임스페이스 멤버 사용하기
네임스페이스 내부에 export된 멤버를 사용하려면, 네임스페이스 이름을 접두사로 붙여 접근해야 합니다.
// 타입 검사를 위해 관련 파일을 컴파일에 포함하고,
// 실행할 때도 합친 출력 또는 올바른 <script> 순서로 구현을 먼저 로드해야 합니다.
let zipValidator = new Validators.ZipCodeValidator();
let lettersValidator = new Validators.LettersOnlyValidator();
let zipCode = "12345";
let text = "Hello";
console.log(`"${zipCode}" is valid zip code: ${zipValidator.isValid(zipCode)}`); // true
console.log(`"${text}" consists of letters only: ${lettersValidator.isValid(text)}`); // true
// console.log(Validators.isLetter("a")); // Error: 'isLetter' 속성이 'typeof Validators' 형식에 없습니다. (export 되지 않아서)네임스페이스의 멤버에 접근하는 방식은 객체의 속성에 접근하는 것과 유사합니다.
Validators.ZipCodeValidator, Validators.LettersOnlyValidator와 같이 사용합니다.
다중 파일 네임스페이스
네임스페이스는 여러 파일에 걸쳐 확장될 수 있습니다.
같은 선언 스코프를 공유하는 스크립트 파일에서는 동일한 이름의 네임스페이스가 병합됩니다. 다음 분할 예제는 앞의 단일 파일 구현을 대체하며, 같은 클래스 선언을 함께 넣지는 않습니다.
namespace Validators {
export class ZipCodeValidator {
isValid(s: string): boolean {
return s.length === 5 && /^\d+$/.test(s);
}
}
}namespace Validators {
export class LettersOnlyValidator {
isValid(s: string): boolean {
return /^[A-Za-z]+$/.test(s);
}
}
}이렇게 두 파일을 컴파일하면, 마치 하나의 파일에 모든 내용이 정의된 것처럼 Validators 네임스페이스를 사용할 수 있습니다.
tsc 명령어 사용 시 모든 관련 파일을 함께 컴파일해야 합니다. (예: tsc Validators.ZipCode.ts Validators.LettersOnly.ts app.ts --outFile output.js)
네임스페이스 별칭
네임스페이스 이름이 길거나 여러 계층으로 중첩되어 있을 경우, import = 문법을 사용하여 별칭(alias)을 부여할 수 있습니다.
이는 코드 가독성을 높이는 데 도움이 됩니다.
namespace MyUtility.DataValidation {
export class PhoneNumberValidator {
isValid(phone: string): boolean {
return phone.length > 7;
}
}
}
// 긴 네임스페이스에 별칭 부여
import Validator = MyUtility.DataValidation;
let phoneValidator = new Validator.PhoneNumberValidator();
console.log(`"123-456-7890" is a valid phone number: ${phoneValidator.isValid("123-456-7890")}`); // true여기서 import Validator = MyUtility.DataValidation;은 ES 모듈을 로드하지 않고 기존의 한정된 이름에 별칭을 붙입니다. import =에는 CommonJS의 require와 결합하는 별도 형식도 있습니다.
네임스페이스 사용의 한계와 ES 모듈로의 전환
네임스페이스는 자바스크립트 파일들이 HTML script 태그를 통해 전역 스코프에 순서대로 로드되는 환경(특히 웹팩과 같은 번들러가 없던 시절)에서 유용했습니다.
그러나 다음과 같은 한계점 때문에 ES 모듈이 더 선호됩니다.
- 컴파일 순서 및 번들링 문제: 여러 파일에 걸쳐 나뉜 네임스페이스를 사용하려면, 컴파일 시 파일들의 순서를 정확히 지정하거나
--outFile옵션을 사용하여 하나의 파일로 번들링해야 하는 복잡성이 있었습니다. ES 모듈은 이러한 의존성 관리를 언어 차원에서 제공하므로 번들러가 훨씬 효율적으로 처리할 수 있습니다. - 전역 스코프 오염 가능성:
export되지 않은 네임스페이스 내부 멤버는 캡슐화되지만, 이 예제처럼 전역 스크립트에 쓰인 네임스페이스의 루트 이름은 전역에 선언됩니다. 외부 모듈 안의 namespace는 모듈 내부에 머뭅니다. 이름 충돌 가능성이 완전히 사라지는 것은 아닙니다. ES 모듈은 파일 자체가 모듈 스코프를 형성하므로, 각 파일 내부의 최상위 레벨 선언도 기본적으로 전역 스코프를 오염시키지 않습니다. - 표준화 부재: 네임스페이스는 타입스크립트 고유의 기능이며 자바스크립트 표준에는 없습니다. 반면 ES 모듈은 자바스크립트의 공식 표준입니다. 표준을 따르는 것이 장기적인 유지보수와 생태계 호환성에 유리합니다.
권장 사항: 새로운 애플리케이션의 파일 의존성은 ES 모듈로 구성하는 편이 명확합니다. 기존 전역·UMD API를 설명하는 ambient namespace는 런타임 값을 생성하지 않는 선언 모델이므로, 실행 코드의 마이그레이션과 구분해 유지 여부를 판단합니다.
ES 모듈은 더 명확한 의존성 관리, 더 나은 트리 셰이킹(Tree Shaking) 지원(사용되지 않는 코드 제거), 그리고 자바스크립트 생태계 전반의 표준화라는 이점을 제공합니다.
네임스페이스는 타입스크립트가 처음 등장했을 때 코드 조직화를 돕는 중요한 기능이었지만, 이제는 ES 모듈 시스템이 그 역할을 대체했습니다.
비록 현대 개발에서는 거의 사용되지 않지만, 기존 코드를 이해하거나 특정 레거시 환경을 다룰 때 네임스페이스의 개념을 알고 있는 것이 여전히 유용할 수 있습니다
레거시 코드를 현대 구조로 옮길 때는 네임스페이스의 전역 이름, export 멤버, 파일 로딩 순서를 ES 모듈의 import/export 경계로 대응시키면 됩니다.
네임스페이스를 유지할지 ES 모듈로 옮길지 결정할 때는 코드 실행 환경, 번들링 방식, 전역 이름 충돌 위험을 함께 따져야 합니다.
네임스페이스를 유지할지 ES 모듈로 옮길지 판단할 때는 이름 충돌, export 범위, 번들링 방식을 마지막으로 점검합니다.