A 10 minute primer to JavaScript modules, module formats, module loaders and module bundlers - Lee-hyuna/33-js-concepts-kr GitHub Wiki

A 10 minute primer to JavaScript modules, module formats, module loaders and module bundlers

μ΅œμ‹ μ˜ JS κ°œλ°œμ€ κ°•λ ₯ν•©λ‹ˆλ‹€.

ν”„λ‘œμ νŠΈλ₯Ό 진행할 λ•Œ μ™œ μ΅œμ‹ μ˜ 도ꡬ와 νˆ΄λ“€μ΄ ν•„μš”ν•œμ§€ κΆκΈˆν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Webpack κ³Ό SystemJS 와 같은 νˆ΄λ“€μ€ λ¬΄μ—‡μΈκ°€μš” ? 또 AMD, UMD, CommonJS λŠ” 무슨 μ˜λ―ΈμΈκ°€μš” ? 그것듀은 무슨 연관이 μžˆλ‚˜μš”? μ™œ ν•„μš”λ‘œ ν•œκ°€μš” ?

이 κΈ€μ—μ„œλŠ” λ‹€λ₯Έ JS λͺ¨λ“ˆμ˜ λͺ¨λ“ˆμ˜ ν˜•μ‹, λ‘œλ” 및 λ²ˆλ“€μ— λŒ€ν•˜μ—¬ ν•™μŠ΅ν•©λ‹ˆλ‹€.

λ‹€λ₯Έ 도ꡬ와 νŒ¨ν„΄λ“€μ— μ΄ν•΄λΏλ§Œ μ•„λ‹ˆλΌ μ΅œμ‹  JS κ°œλ°œμ— λŒ€ν•œ κ°œλ…λ„ 이해할 수 μžˆμ„ 것 μž…λ‹ˆλ‹€.

What is a module?

λͺ¨λ“ˆμ΄λž€ μž¬μ‚¬μš© ν•  수 μžˆλŠ” μ½”λ“œ μ‘°κ°μž…λ‹ˆλ‹€. 그것 μ„ΈλΆ€ κ΅¬ν˜„μ„ μΊ‘μŠν™”ν•˜κ³  public api 을 λ…ΈμΆœν•˜κ³  μ‚¬μš©ν•˜κΈ° μ‰½κ²Œ ν•©λ‹ˆλ‹€.

Why do we need modules?

λͺ¨λ“ˆ 없이 μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λͺ¨λ“ˆνŒ¨ν„΄μ€ κ°œλ°œμžκ°€ 60λ…„λŒ€ 70λ…„λŒ€ 이후 λ‹€μ–‘ν•œ ν˜•νƒœμ™€ ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄λ‘œ μ‚¬μš©ν•΄μ˜¨ νŒ¨ν„΄μž…λ‹ˆλ‹€.

JS λͺ¨λ“ˆμ€ λ‹€μŒκ³Ό 같은 ν˜•νƒœκ°€ μ΄μƒμ μž…λ‹ˆλ‹€:

  • 좔상화: λΌμ΄λΈŒλŸ¬λ¦¬μ— κΈ°λŠ₯을 μœ„μž„ν•©λ‹ˆλ‹€. κ΅¬ν˜„μ˜ λ³΅μž‘μ„±μ„ 이해할 ν•„μš” μ—†μŠ΅λ‹ˆλ‹€.
  • μΊ‘μŠν™”: λͺ¨λ“ˆ λ‚΄λΆ€μ˜ μ½”λ“œλ₯Ό μˆ¨κΉλ‹ˆλ‹€.
  • μž¬μ‚¬μš©: 동일 μ½”λ“œμ˜ λ°˜λ³΅μ„ ν”Όν•©λ‹ˆλ‹€.
  • μ˜μ‘΄μ„±: μ˜μ‘΄μ„±μ„ μ‰½κ²Œ λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Module patterns in ES5

ES5 이전 버전은 λͺ¨λ“ˆμ„ μ—Όλ‘ν•˜μ—¬ μ„€κ³„λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. κ°œλ°œμžλ“€μ€ JS μ—μ„œμ˜ λͺ¨λ“ˆνŒ¨ν„΄μ„ κ΅¬ν˜„ν•˜κΈ°μœ„ν•΄ λ‹€μ–‘ν•œ 방법을 μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€.

두 κ°€μ§€μ˜ μ‰¬μš΄ νŒ¨ν„΄μ„ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€. μ¦‰μ‹œ μ‹€ν–‰ν•¨μˆ˜:

(function(){
  // ...
})()

IIFE λŠ” μ„ μ–Έ 될 λ•Œ ν˜ΈμΆœλ˜λŠ” 읡λͺ… ν•¨μˆ˜μž…λ‹ˆλ‹€.

JS μ—μ„œλŠ” function μ΄λΌλŠ” λ‹¨μ–΄λ‘œ μ‹œμž‘ν•˜λŠ” 쀄을 ν•¨μˆ˜ μ„ μ–ΈμœΌλ‘œ νŒλ‹¨ν•©λ‹ˆλ‹€.

// Function declaration
function(){  
  console.log('test');
}

μ¦‰μ‹œ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

// Immediately Invoked Function Declaration
function(){  
  console.log('test');
}()

// => Uncaught SyntaxError: Unexpected token )

ν•¨μˆ˜λ₯Ό κ΄„ν˜Έλ‘œ 감싸주면 ν•¨μˆ˜ ν‘œν˜„μ‹μ΄ λ©λ‹ˆλ‹€.

// Function expression
(function(){
  console.log('test');
})

// => returns function(){ console.log('test') }

ν•¨μˆ˜ ν‘œν˜„μ‹μ€ ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜λ―€λ‘œ μ¦‰μ‹œ 호좜이 κ°€λŠ₯ν•΄μ§‘λ‹ˆλ‹€.

// Immediately Invoked Function Expression
(function(){
  console.log('test');
})()

// => writes 'test' to the console and returns undefined

μ¦‰μ‹œ ν•¨μˆ˜λ₯Ό λΆ€λ₯΄λ©΄ λ‹€μŒκ³Ό 같은 λ™μž‘μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€.

IIFE μ•ˆμ—μ„œ μ½”λ“œλ₯Ό μΊ‘μŠν™”ν•©λ‹ˆλ‹€. IIFE λ‚΄μ—μ„œ λ³€μˆ˜λ₯Ό μ •μ˜ν•˜κΈ° λ•Œλ¬Έμ— μ „μ—­ λ²”μœ„λ₯Ό μ˜€μ—Όμ‹œν‚€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜, 쒅속성에 λŒ€ν•œ 관리λ₯Ό μ œκ³΅ν•΄μ£Όμ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€.

Revealing Module

Revealing Module νŒ¨ν„΄μ€ IIFE 와 μœ μ‚¬ ν•˜μ§€λ§Œ λ°˜ν™˜λœ 값을 λ³€μˆ˜μ— ν• λ‹Ήν•©λ‹ˆλ‹€.

// Expose module as global variable
var singleton = function(){

  // Inner logic
  function sayHello(){
    console.log('Hello');
  }

  // Expose API
  return {
    sayHello: sayHello
  }
}()

function μ΄λΌλŠ” 단어가 μ€„μ˜ μ‹œμž‘μ— μžˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— κ΄„ν˜Έκ°€ ν•„μš” μ—†μŠ΅λ‹ˆλ‹€. λ³€μˆ˜λ₯Ό ν†΅ν•˜μ—¬ λͺ¨λ“ˆμ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

// Access module functionality
singleton.sayHello();  
// => Hello

singleton λŒ€μ‹  μƒμ„±μž ν•¨μˆ˜λ₯Ό λ…ΈμΆœ ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

// Expose module as global variable
var Module = function(){

  // Inner logic
  function sayHello(){
    console.log('Hello');
  }

  // Expose API
  return {
    sayHello: sayHello
  }
}

μ„ μ–Έμ‹œ ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜μ§€ μ•ŠλŠ”μ§€ μ£Όμ˜ν•΄μ•Όν•©λ‹ˆλ‹€. μƒμ„±μž ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ λͺ¨λ“ˆμ„ μΈμŠ€ν„΄μŠ€ν™” ν•©λ‹ˆλ‹€.

var module = new Module();  

public api 에 μ ‘κ·Όν•˜λ €λ©΄ μ•„λž˜μ™€ 같이 ν•©λ‹ˆλ‹€.

module.sayHello();  
// => Hello

IIFE 와 λΉ„μŠ·ν•œ 이점을 μ œκ³΅ν•©λ‹ˆλ‹€. 쒅속성 κ΄€λ¦¬λŠ” μ§€μ›ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

JS κ°€ λ°œμ „λ¨μ— 따라 λ§Žμ€ ꡬ문이 λ§Œλ“€μ–΄μ§‘λ‹ˆλ‹€. μ΄λŠ” 각각 μž₯점과 단점이 μžˆμŠ΅λ‹ˆλ‹€.

μš°λ¦¬λŠ” 이것을 λͺ¨λ“ˆ ν˜•μ‹μ΄λΌκ³  λΆ€λ¦…λ‹ˆλ‹€.

Module formats

λͺ¨λ“ˆ ν˜•μ‹μ΄λž€ λͺ¨λ“ˆμ„ μ •μ˜ν•˜λŠ”λ° μ‚¬μš©ν•  수 μžˆλŠ” κ΅¬λ¬Έμž…λ‹ˆλ‹€. ES6 μ΄μ „μ—λŠ” JS 에 정식 λͺ¨λ“ˆ ν˜•μ‹μ΄ μ—†μ—ˆμŠ΅λ‹ˆλ‹€. κ°œλ°œμžλ“€μ€ λ‹€μ–‘ν•œ ν˜•μ‹μœΌλ‘œ JS μ—μ„œ λͺ¨λ“ˆμ„ μ •μ˜ν–ˆμ—ˆμŠ΅λ‹ˆλ‹€.

많이 μ±„νƒλ˜κ³  μ•Œλ €μ§„ ν˜•μ‹μ€ μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

  • Asynchronous Module Definition (AMD)
  • CommonJS
  • Universal Module Definition (UMD)
  • System.register
  • ES6 module format

각각의 ꡬ문을 κ°„λž΅ν•˜κ²Œ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

Asynchronous Module Definition (AMD)

AMD ν˜•μ‹μ€ λΈŒλΌμš°μ €μ—μ„œ μ‚¬μš©λ˜λ©° μ •μ˜ ν•¨μˆ˜λ₯Ό λͺ¨λ“ˆμ„ μ •μ˜ν•©λ‹ˆλ‹€.

//Calling define with a dependency array and a factory function
define(['dep1', 'dep2'], function (dep1, dep2) {

    //Define the module value by returning a value.
    return function () {};
});

CommonJS format

CommonJs λŠ” Node μ—μ„œ μ‚¬μš©λ˜λ©° require 및 module exports λ₯Ό μ‚¬μš©ν•˜μ—¬ 쒅속성과 λͺ¨λ“ˆμ„ κ΄€λ¦¬ν•©λ‹ˆλ‹€.

var dep1 = require('./dep1');  
var dep2 = require('./dep2');

module.exports = function(){  
  // ...
}

Universal Module Definition (UMD)

λΈŒλΌμš°μ €μ™€ Node μ—μ„œ λͺ¨λ‘ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
      define(['b'], factory);
  } else if (typeof module === 'object' && module.exports) {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory(require('b'));
  } else {
    // Browser globals (root is window)
    root.returnExports = factory(root.b);
  }
}(this, function (b) {
  //use b in some fashion.

  // Just return a value to define the module export.
  // This example returns an object, but the module
  // can return a function as the exported value.
  return {};
}));

System.register

ES5 μ—μ„œ ES6 λͺ¨λ“ˆ ꡬ문을 μ§€μ›ν•˜λ„λ‘ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€

ES6 module format

ES6 μ—μ„œ JS λŠ” 기본적으둜 λͺ¨λ“ˆ ν˜•μ‹μ„ μ§€μ›ν•©λ‹ˆλ‹€. export token 을 μ΄μš©ν•˜μ—¬ api λ₯Ό λ‚΄λ³΄λƒ…λ‹ˆλ‹€.

// lib.js

// Export the function
export function sayHello(){  
  console.log('Hello');
}

// Do not export the function
function somePrivateFunction(){  
  // ...
}

export 된 λͺ¨λ“ˆμ„ κ°€μ Έμ˜€κΈ° μœ„ν•œ:

import { sayHello } from './lib';

sayHello();  
// => Hello

alias λ₯Ό μ΄μš©ν•˜μ—¬ κ°€μ Έ 올 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

import { sayHello as say } from './lib';

say();  
// => Hello

ν•œλ²ˆμ— 전체 λͺ¨λ“ˆμ„ κ°€μ Έμ˜¬ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

import * as lib from './lib';

lib.sayHello();  
// => Hello

default export 도 μ§€μ›ν•©λ‹ˆλ‹€.

// lib.js

// Export default function
export default function sayHello(){  
  console.log('Hello');
}

// Export non-default function
export function sayGoodbye(){  
  console.log('Goodbye');
}

λ‹€μŒκ³Ό 같이 κ°€μ Έμ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

import sayHello, { sayGoodbye } from './lib';

sayHello();  
// => Hello

sayGoodbye();  
// => Goodbye

ν•¨μˆ˜λΏλ§Œμ•„λ‹ˆλΌ μ›ν•˜λŠ” 것을 내보낼 수 μžˆμŠ΅λ‹ˆλ‹€.

// lib.js

// Export default function
export default function sayHello(){  
  console.log('Hello');
}

// Export non-default function
export function sayGoodbye(){  
  console.log('Goodbye');
}

// Export simple value
export const apiUrl = '...';

// Export object
export const settings = {  
  debug: true
}

ES6 의 λͺ¨λ“ˆν˜•μ‹μ€ μ•ˆνƒ€κΉκ²Œλ„ λͺ¨λ“  λΈŒλΌμš°μ €μ—μ„œ μ§€μ›λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ES6 λͺ¨λ“ˆ 포맷을 μ‚¬μš©ν•˜κΈ° μœ„ν•΄ ES5 λ¬Έλ²•μœΌλ‘œ λ³€ν™˜ν•˜κΈ° μœ„ν•œ Babel 같은 λ³€ν™˜κΈ°κ°€ ν•„μš”ν•©λ‹ˆλ‹€.

Module loaders

λͺ¨λ“ˆ λ‘œλ”λŠ” νŠΉμ • λͺ¨λ“ˆμ„ ν˜•μ‹μœΌλ‘œ μž‘μ„±λœ λͺ¨λ“ˆμ„ ν•΄μ„ν•˜κ³  μ½μŠ΅λ‹ˆλ‹€.

λŸ°νƒ€μž„μ— λͺ¨λ“ˆ λ‘œλ”κ°€ μ‹€ν–‰λ©λ‹ˆλ‹€.

  • λΈŒλΌμš°μ €μ— λͺ¨λ“ˆ λ‘œλ”λ₯Ό λ‘œλ“œν•©λ‹ˆλ‹€.
  • μ–΄λ–€ νŒŒμΌμ„ λ‘œλ“œν•  것인지 μ•Œλ €μ€λ‹ˆλ‹€.
  • μ•± νŒŒμΌμ„ λ‹€μš΄λ‘œλ“œν•˜κ³  ν•΄μ„ν•©λ‹ˆλ‹€.
  • ν•„μš”ν•œ 경우 νŒŒμΌμ„ λ‹€μš΄λ‘œλ“œν•©λ‹ˆλ‹€.

λΈŒλΌμš°μ €μ˜ 개발자 μ½˜μ†”μ—μ„œ λ„€νŠΈμ›Œν¬ 탭을 보면 λ§Žμ€ 파일이 ν•„μš”μ— 따라 λͺ¨λ“ˆ λ‘œλ”μ— λ‘œλ“œλœλ‹€λŠ” 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. 많이 μ‚¬μš©λ˜μ–΄μ§€λŠ” λͺ¨λ“ˆ λ‘œλ”λŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

  • RequireJS: AMD ν˜•μ‹μ˜ λͺ¨λ“ˆ λ‘œλ”
  • SystemJS: AMD, common JS, UMD λ˜λŠ” System.gister ν˜•μ‹μ˜ λͺ¨λ“ˆ λ‘œλ”

Module bundlers

λͺ¨λ“ˆ λ²ˆλ“€λŸ¬λŠ” λͺ¨λ“ˆ λ‘œλ”λ₯Ό λŒ€μ²΄ν•©λ‹ˆλ‹€.

λͺ¨λ“ˆ λ‘œλ”μ™€ 달리 λ²ˆλ“€μ€ λΉŒλ“œμ‹œμ— μ‹€ν–‰λ©λ‹ˆλ‹€.

λΉŒλ“œν•  λ•Œ λͺ¨λ“ˆ λ²ˆλ“€μ„ μ‹€ν–‰ν•˜μ—¬ λ²ˆλ“€ νŒŒμΌμ„ μƒμ„±ν•©λ‹ˆλ‹€. λΈŒλΌμš°μ €μ—μ„œ λ²ˆλ“€νŒŒμΌμ„ λ‘œλ“œν•©λ‹ˆλ‹€. λ„€νŠΈμ›Œν¬ 탭을 ν™•μΈν•˜λ©΄ ν•˜λ‚˜μ˜ 파일만 λ‘œλ“œν•©λ‹ˆλ‹€. λΈŒλΌμš°μ €μ—λŠ” λͺ¨λ“ˆ λ‘œλ”κ°€ ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λͺ¨λ“  μ½”λ“œλŠ” λ²ˆλ“€νŒŒμΌμ— ν¬ν•¨λ˜μ–΄μ ΈμžˆμŠ΅λ‹ˆλ‹€.

많이 μ“°μ΄λŠ” λͺ¨λ“ˆ λ²ˆλ“€μ€ μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

  • Browserify: bundler for CommonJS modules
  • Webpack: bundler for AMD, CommonJS, ES6 modules