본문 바로가기

Web + APP/JavaScript

TypeScript Modules

반응형
SMALL

안녕하세요 ! 꼬동입니다.

 

주말에 할 건 없고, 글이나 쓸까하여 카페에 와보았습니다.

 

무슨 글을 쓸까 하다가....

 

모듈에 관련해서 글을 써야겠다고 생각이 들어서 !

 

모듈에 대해 써보겠읍니다.

 

모듈하면 떠오르는 이미지


개발을 하다보면, 모듈이라는 단어를 많이 접하게 됩니다.

 

추상적으로 해석하면, 모듈이라는 것이 하나의 기능 블록이라고 볼 수 있는데요.

 

하지만 우리는 전문적인 프로 개발자 어디 가서 "모듈은 하나의 기능 블록" 입니다.

 

라고 말하기엔 너무 짜치죠.

 

좀 더 세부적으로 알아보자고요.

 


애초에 모듈이라는게 건축학 쪽에서 쓰던 단어입니다.

 

16세기 때부터 써왔던 단어였는데, 이게 컴퓨터 공학 쪽에서도 쓰이게 된것이죠.

 

사실 개발이라는게 하나의 건물을 짓는거와 다름이 없으니까, 이를 그대로 써도 의미상 크게 다르지 않아서 쓸 수 있게 된거 같은데요.

 

그 의미로는 하나의 SW / HW 단위를 지칭하는데 사용됩니다.

 

그 단위라는 것은 클 수도 있고 작을 수 있는데, 보통 문제를 모듈화 한다고 하면 거대한 문제를 작은 조각의 문제로 나누어 다루기 쉽도록 하는 과정입니다.

 

그렇기 때문에 기능적으로 분리가 되어 있고 독립적인 일을  수행하는 작은 단위를 모듈이라고 볼 수 있죠.


JavaScript에도 ES6 (ECMAScript 2015)부터 모듈이라는 개념을 도입했습니다.

 

그리고 TypeScript에서는 이 개념을 공유하죠.

 

그 전엔 기본적으로 모두 변수와 함수, 타입이 전역적으로 사용되기 때문에, 서로 충돌하거나 예기치 않게 다른 파일의 변수를 변경하는 문제가 발생할 수 있습니다.

 

하지만 모듈은 자체 스코프 내에서 실행됩니다.

 

모듈 내에서 선언된 변수 / 함수 / 클래스 등은 export 하지 않는 한 모듈 외부에는 보이지 않으며, 다른 모듈에서는 export한 변수 / 함수 / 클래스 / 인터페이스 등을 import하지 않는 한 사용하지 못합니다.

 

그렇기 때문에 import 또는 export가 있는 파일을 모듈로 취급할 수 있겠죠.

 

그나저나 문득 궁금해졌는데, ES6부터 나온 모듈 개념이 이 전에는 어떻게 활용되고 있었을까요 ?

 

그 전에는 CommonJS로 모듈 개념을 사용했다고 합니다. (삼성에서 이렇게 했던걸로 기억이 나군요 / 거기서 IE를 썼으니)

 

아래와 같은 방법입니다.

 

// CommonJS
const moment = require("moment");

// ES6
import moment from "moment";

 

추가로 import가 좀 더 성능이 우수하며 메모리를 절약한다고 하네요. (트리 쉐이킹)

 

간단한 사용법은 아래에 나와있으니 참고를 하면 될 거 같습니다.

 

https://www.typescriptlang.org/ko/docs/handbook/modules.html

 

Documentation - Modules

How modules work in TypeScript

www.typescriptlang.org

 

 

TS Compiler의 경우 지정된 모듈 대상에 따라

  • Node.js (CommonJS)
  • require.js (AMD)
  • UMD
  • SystemJS
  • ECMAScript 2015 native modules (ES6)

모듈 - 로딩 시스템에 적합한 코드를 생성해줍니다.

 

define / require / register 호출 기능에 적절히 매칭을 시켜준다는 뜻입니다.

 

// SimpleModules.ts
import m = require("mod");
export let t = m.something + 1;

// AMD / RequireJS SimpleModule.js
define(["require", "exports", "./mod"], function (require, exports, mod_1) {
    exports.t = mod_1.something + 1;
});

// CommomJS / Node SimpleModule.js
var mod_1 = require("./mod");
exports.t = mod_1.something + 1;

// UMD SimpleModule.js
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports); if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./mod"], factory);
    }
})(function (require, exports) {
    var mod_1 = require("./mod");
    exports.t = mod_1.something + 1;
});

// System SimpleModule.js
System.register(["./mod"], function(exports_1) {
    var mod_1;
    var t;
    return {
        setters:[
            function (mod_1_1) {
                mod_1 = mod_1_1;
            }],
        execute: function() {
            exports_1("t", t = mod_1.something + 1);
        }
    }
});

// Native ECMAScript 2015 modules SimpleModule.js
import { something } from "./mod";
export var t = something + 1;

Ambient 모듈이라 불리는 모듈도 존재합니다.

 

TypeScript는 JavaScript와는 다르게 구현 (implementation) 된 소스코드와 선언 (declaration) 된 소스코드로 분리를 할 수 있는데요.

 

일반적으로 .ts 파일을 .js와 .d.ts 파일로 tsc가 컴파일을 하는데, 만약 JavaScript로만 짜여진, 라이브러리를 저희가 가져와서 사용한다고 했을 때, 수동으로 .d.ts 파일을 수동으로 만들어주어야합니다.

 

즉, tsc가 컴파일 할 때만 사용하는 파일이 .d.ts 파일입니다.

 

직접 구현을 해본적은 없어서, 여기까지 겉햝기만 해봅시다.

 

그 더러운거 저리 치워줄래


모듈 설계 가이드

TypeScript 공식 문서에서 제공하는 모듈 설계 가이드 입니다.

 

원래 가이드까지는 안 적으려고 했는데, 만든 사람이 써라는데 ㅋㅋㅋㅋㅋㅋ 알아두면 좋을거 같아서, 써봅니다.

 

1. 가능한 최상위-레벨에 가깝게 export 하기

모듈의 사용자가 중첩 수준이 과도하게 추가된 모듈을 사용하기엔 힘든 부분이 많습니다.

 

namespace를 export 하지 않는 것이 중첩 레이어를 줄일 수 있는 방법이며, 클래스의 정적 메서드를 export하지 않도록 합시다.

 

이런 경우 간단하게 헬퍼 함수를 export 할 수 있도록 합시다.

 

2. 단일 class나 function을 export할 경우, export default 사용하기

모듈의 주요 목적이 한 개의 특정 export를 저장하는 것이라면, default export를 사용합시다.

 

이러면 import하기 더 편하니까요.

 

// MyClass.ts
export default class SomeType {
  constructor() { ... }
}

// MyFunc.ts
export default function getThing() { return "thing"; }

// Consumer.ts
import t from "./MyClass";
import f from "./MyFunc";
let x = new t();
console.log(f());

이러면 과도하게 레이어를 타고 들어가서 원하는 기능을 사용 안해도 됩니다. ( ex : new MyClass.SomeType())

 

3. 여러 객체를 export 하는 경우, 최상위 - 레벨에 두세요.

4. 많은 것을 import 하는 경우, 네임스페이스 import 패턴을 사용하세요.

// MyLargeModule.ts
export class Dog { ... }
export class Cat { ... }
export class Tree { ... }
export class Flower { ... }

// Consumer.ts
import * as myLargeModule from "./MyLargeModule.ts";

let x = new myLargeModule.Dog();

종종 모듈의 기능을 확장해야하는 경우가 있는데요.

 

이런 경우 기존의 객체를 변형하지 않고 새로운 기능을 제공하는 개체를 export 하는 방법을 사용합니다.

 

https://www.typescriptlang.org/ko/docs/handbook/modules.html#%EC%83%81%EC%86%8D%EC%9D%84-%EC%9C%84%ED%95%9C-re-export-%ED%95%98%EA%B8%B0-re-export-to-extend

 

Documentation - Modules

How modules work in TypeScript

www.typescriptlang.org

 

위의 URL에 해당 예시 코드가 있는데요. 간단하게, 기존 모듈을 import해서 그 모듈을 상속받고, 오버라이딩을 하여 상속 받은 모듈을 export 하는 예시입니다.

 

import { Calculator } from './Calculator';

class ProgrammerCaculator extends Calculator {
	...
}

export { ProgrammerCacluator as Calculator };
export { test } from './Calculator";

 


이상 TypeScript Modules 였습니다. ^_^

반응형
LIST