본문 바로가기
프로그래밍 언어/Javascript

자바스크립트 모듈

by pagehit 2021. 8. 12.
반응형

Node는 closure를 기반으로한 모듈을 지원하며, require() 함수를 이용한다. 하지만 이는 자바스크립트 표준에서 채택되지 않았으며 ES6는 import와 export 키워드를 사용하는 것을 정의했다. Node에서도 최근에 이를 구현했다. 실용적인 관점에서 자바스크립트의 모듈은 코드 번들링 도구에 여전히 많이 의존하고 있다.

 

immediately invoked function을 이용해 모듈을 구현할 수 있다. utility function을 함수 안으로 숨기고 모듈의 public API를 반환값으로 한다.

const BitSet = (function() { // Set BitSet to the return value of this function
    // Private implementation details here
    function isValid(set, n) { ... }
    function has(set, byte, bit) { ... }
    const BITS = new Uint8Array([1, 2, 4, 8, 16, 32, 64, 128]);
    const MASKS = new Uint8Array([~1, ~2, ~4, ~8, ~16, ~32, ~64, ~128]);

    // The public API of the module is just the BitSet class, which we define
    // and return here. The class can use the private functions and constants
    // defined above, but they will be hidden from users of the class
    return class BitSet extends AbstractWritableSet {
        // ... implementation omitted ...
    };
}());

 

// This is how we could define a stats module
const stats = (function() {
    // Utility functions private to the module
    const sum = (x, y) => x + y;
    const square = x => x * x;

    // A public function that will be exported
    function mean(data) {
        return data.reduce(sum)/data.length;
    }

    // A public function that we will export
    function stddev(data) {
        let m = mean(data);
        return Math.sqrt(
            data.map(x => x - m).map(square).reduce(sum)/(data.length-1)
        );
    }

    // We export the public function as properties of an object
    return { mean, stddev };
}());

// And here is how we might use the module
stats.mean([1, 3, 5, 7, 9])   // => 5
stats.stddev([1, 3, 5, 7, 9]) // => Math.sqrt(10)

 

closure를 이용한 module

const modules = {};
function require(moduleName) { return modules[moduleName]; }

modules["sets.js"] = (function() {
    const exports = {};

    // The contents of the sets.js file go here:
    exports.BitSet = class BitSet { ... };

    return exports;
}());

modules["stats.js"] = (function() {
    const exports = {};

    // The contents of the stats.js file go here:
    const sum = (x, y) => x + y;
    const square = x = > x * x;
    exports.mean = function(data) { ... };
    exports.stddev = function(data) { ... };

    return exports;
}());

 

// Get references to the modules (or the module content) that we need
const stats = require("stats.js");
const BitSet = require("sets.js").BitSet;

// Now write code using those modules
let s = new BitSet(100);
s.insert(10);
s.insert(20);
s.insert(30);
let average = stats.mean([...s]); // average is 20

 

위 코드는 webpack이나 parcel같은 코드 번들링 도구가 동작하는 방식을 간단히 나타낸 것이며, Node에서 require() 함수가 동작하는 방식도 보여주고 있다.

 

Node에서의 module

Node에서는 프로그램을 여러 파일로 나눠 구성한다. 웹 브라우저가 느린 네트워크를 통해 자바스크립트 파일을 읽는 것과 달리 Node에서는 하나의 파일로 번들 시키는 이점이 없기 때문이다.

Node에서 각 파일은 상수, 변수, 함수, 클래스를 직접 export하지 않는 이상 private namespace를 갖는 독립적인 모듈이다. 하나의 모듈에서 export한 값을 import한 모듈에서만 이 값들이 보인다.

require() 함수를 통해 다른 모듈을 import하고, Export 객체의 속성을 설정하거나 module.exports 객체를 바꿔서 public API를 export한다.

const sum = (x, y) => x + y;
const square = x => x * x;

exports.mean = data => data.reduce(sum)/data.length;
exports.stddev = function(d) {
    let m = exports.mean(d);
    return Math.sqrt(d.map(x => x - m).map(square).reduce(sum)/(d.length-1));
};

 

아래와 같이 전체 함수나 클래스 대신에 하나의 함수나 클래스를 export할 수 있다.

module.exports = class BitSet extends AbstractWritableSet {
    // implementation omitted
};

 

module.exports는 exports와 같은 객체를 가리킨다. exports.mean 대신에 module.exports.mean으로 할당할 수 있다.

 

아래와 같이 함수를 하나씩 export하는 대신에 파일의 끝에서 하나의 객체를 export 할 수 있다.

// Define all the functions, public and private
const sum = (x, y) => x + y;
const square = x => x * x;
const mean = data => data.reduce(sum)/data.length;
const stddev = d => {
    let m = mean(d);
    return Math.sqrt(d.map(x => x - m).map(square).reduce(sum)/(d.length-1));
};

// Now export only the public ones
module.exports = { mean, stddev };

 

 

반응형

댓글