안녕하세요. IT 엘도라도 에 오신 것을 환영합니다.
글을 쓰는 것은 귀찮지만 다시 찾아보는 것은 더 귀찮습니다.
완전한 나만의 것으로 만들기 위해 지식을 차곡차곡 저장해 보아요.   포스팅 둘러보기 ▼

타입스크립트 (TypeScript)

[TypeScript] module, import, export, declare 개념 정리

피그브라더 2020. 12. 30. 14:24

👉 모듈(Module)이란?

  • import 또는 export가 있는 파일은 모듈(Module)로 취급이 된다.
  • 즉, 외부에서는 직접적으로 모듈을 불러오지 않는 이상 그 모듈의 데이터를 사용할 수 없다.
  • import는 모듈에서 데이터를 불러올 때, export는 모듈에서 데이터를 내보낼 때 사용한다.

 

👉 import, export 데이터 : 변수, 상수, 함수, 클래스, 타입, 네임스페이스

  • TypeScript 컴파일러가 모듈 로더를 통해 실제로 불러오는 건 오로지 타입 정보뿐이다.
  • 변수, 상수, 함수, 클래스 : JS 모듈 로더 코드로 컴파일이 된다.
  • 타입 : JS 코드로는 컴파일 되지 않는다. 즉, JS 코드에서는 삭제된다.
  • 네임스페이스 : JS 일반 객체로 컴파일 된다. IIFE 함수에 해당 객체를 전달하고 그 함수를 즉시 호출하는 식으로 컴파일 된다. 기본적으로 네임스페이스 외부에선 네임스페이스 내부를 볼 수 없는데, 이러한 동작은 IIFE 함수 내에서 지역 변수를 선언하며 각종 코드를 실행시키는 방식을 통해 구현하게 된다. 만약 네임스페이스 외부에서도 접근이 가능하도록 하고 싶은 데이터(변수, 상수, 함수, 클래스, 네임스페이스)가 있다면 export를 사용하자.
    // TypeScript
    namespace N1 {
        export let a: string = 'a';
        let b: string = 'b';
        console.log(a);
        console.log(b);
    
        export namespace N2 {
            export let c: string = 'c';
            let d: string = 'd';
            console.log(c);
            console.log(d);
        }
    }
    
    // Compiled JavaScript
    "use strict";
    var N1;
    (function (N1) {
        N1.a = 'a';
        let b = 'b';
        console.log(N1.a);
        console.log(b);
    
        let N2;
        (function (N2) {
            N2.c = 'c';
            let d = 'd';
            console.log(N2.c);
            console.log(d);
        })(N2 = N1.N2 || (N1.N2 = {}));
    })(N1 || (N1 = {}));

 

👉 declare 키워드

  • 변수, 상수, 함수, 또는 클래스가 어딘가에 이미 선언되어 있음을 알린다.
  • 즉, JS 코드로는 컴파일 되지 않고, TypeScript 컴파일러에게 타입 정보를 알리기만 한다.
  • 타입의 경우 어차피 JS 코드로 컴파일 되지 않으므로 declare 키워드를 사용하지 않아도 된다.
  • declare 블록 (declare namespace, declare module, declare global)
    • 앰비언트 컨텍스트(Ambient Context)로 정의되는 영역이다.
    • 이 영역 안에서는 declare 키워드가 기본으로 붙는다. 즉 굳이 또 붙여줄 필요가 없다.
    • 또한 이 영역 안에서는 선언 코드가 아닌 일반 코드를 작성할 수 없다.
    • declare namespace ABC : 원래대로라면 JS 일반 객체로 컴파일 되는 네임스페이스겠지만, declare 키워드를 붙여줌으로써 JS 코드로 컴파일 되지 않게 한다. 일반적으로는 몇몇 타입들을 의미적으로 묶고 싶은 경우에 사용한다. 이 블록은 앰비언트 네임스페이스 혹은 내부 모듈이라고도 부른다.
    • declare module "ABC" : 앰비언트 모듈 선언(Ambient Module Declaration) 파일에 작성하는 블록으로, 앰비언트 모듈 혹은 외부 모듈이라고도 부른다. 이러한 앰비언트 모듈 선언 파일은 컴파일 대상에 포함되기만 한다면(예를 들어 Triple Slash 디렉티브를 사용한다든가 해서) 그곳에 선언된 모듈(여기서는 ABC)의 타입 정보를 참조할 수 있게 된다. 물론 이 블록도 네임스페이스와 마찬가지로 export를 붙인 필드만 외부에서 참조할 수 있다.
    • declare global : 모듈 파일에서도 전역 참조가 가능한 선언 코드를 작성하고 싶을 때 사용한다. 전역 참조가 가능하다는 것은 해당 선언의 참조를 위해 별도의 불러오기 코드가 필요 없다는 뜻이다. 참고로 이 블록은 오로지 declare module 블록 안에서만 중첩이 가능하다.

 

👉 .d.ts 파일 (선언 코드만 담긴 파일)

  • 구현부가 아닌 선언부만을 작성하는 용도의 파일을 의미한다.
  • JS 코드로 컴파일 되지 않는다.
  • skipLibCheck 프로퍼티가 false라면 다음 규칙들을 강제한다. (true여도 지키는 것이 좋다.)
    • 선언 코드만 작성이 가능하고, 일반 코드는 작성할 수 없다.
    • 따라서 최상위에 존재하는 변수, 상수, 함수, 클래스, 네임스페이스의 선언 앞에는 반드시 declare 혹은 export가 붙어야 한다.
  • 이 파일에 작성되는 declare namespace 블록과 declare module 블록의 필드들에는 export 키워드가 기본으로 붙는다. 즉 굳이 또 붙여줄 필요가 없다.

 

👉 export = & import = require()

  • CommonJS와 AMD라는 모듈 시스템에서는 exports 객체라는 개념이 존재한다.
  • exports는 모듈로부터 내보내지는 데이터들을 담고 있는 하나의 객체이다.
  • 그리고 exports 객체를 또 다른 커스텀 객체로 대체하는 것도 지원한다.
  • 사실 export default가 이 동작을 대체하기 위해 등장한 것이지만, 이 둘은 호환성이 좋지 않다.
  • 그래서 TypeScript는 CommonJS와 AMD에서 exports 객체를 또 다른 커스텀 객체로 대체하는 동작을 유사하게 따라할 수 있도록 export = 문법을 지원하고 있다.
  • export = 문법은 모듈로부터 내보내질 하나의 객체를 지정한다. 그것은 클래스일 수도 있고, 인터페이스일 수도 있고, 네임스페이스일 수도 있고, 열거형일 수도 있다.
  • export = 문법으로 객체를 내보낸 모듈의 경우 반드시 import module = require("module") 문법으로만 불러와야 한다.
  • 단, module 프로퍼티가 esnext이면 이 문법을 사용할 수 없다며 에러가 발생한다. 다만 .d.ts 파일에서는 에러가 발생하지 않는다. (그 이유는 아직 파악하지 못했다.)

 

👉 export as namespace

  • .d.ts 파일에서만 사용할 수 있는 문법이다.
  • export 되는 데이터들을 하나의 네임스페이스로 묶어서 export 시키는 문법이다.
  • 그렇게 export 되는 네임스페이스는 전역 참조가 가능하다. 즉 불러오기 코드가 필요 없다.