... ...

이곳에서 발췌한 내용을 번역. https://betterprogramming.pub/what-are-cjs-amd-umd-esm-system-and-iife-3633a112db62

 

What Are CJS, AMD, UMD, ESM, System, and IIFE?

Module formats illustrated with Rollup examples

betterprogramming.pub

최신 JavaScript 프로젝트는 작은 코드 조각을 라이브러리나 애플리케이션과 같이 더 크고 복잡한 것으로 컴파일하기 위한 번들러가 필요합니다. 인기 있는 번들러는 webpack, Rollup, Parcel, RequireJS, Browserify입니다. 이들은 JavaScript 코드를 하나의 번들로 로드할 수 있는 모듈로 변환합니다.

번들은 다양한 형식으로 배열할 수 있습니다. 이 글에서는 CJS, AMD, UMD, ESM, System, IIFE 형식의 실제 예를 제시합니다.

번들러와 포맷

이는 5번째 줄에 스타일시트가 포함된 표준 HTML 파일이며 6번째 줄에 JavaScript 파일이 포함되어 있습니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <link rel="stylesheet" href="style.css" />
    <script src="src/index.js"></script>
  </head>
  <body>
    ... 
  </body>
</html>

모든 JavaScript 코드를 하나의 파일에 넣는 것은 간단한 사례에 효과적입니다. 프로젝트가 확장됨에 따라 별도의 네임스페이스가 있는 독립적인 모듈로 코드를 모듈화해야 합니다. 더 나은 구성 외에도 모듈화는 캡슐화, 종속성 관리 및 재사용 가능성을 제공합니다.

이것이 번들러가 이러한 상황을 해결하는 방식입니다. 스타일시트 및 이미지와 함께 작은 JavaScript 코드를 라이브러리나 애플리케이션과 같이 더 크고 복잡한 것으로 컴파일해야 합니다.

번들러는 번들된 코드를 출력으로 어떻게 포맷해야 합니까? 여러 가지 선택 사항이 있으며 Rollup에서 정의한 포맷은 다음과 같습니다.

  • cjs(CommonJS) — Node 및 기타 번들러에 적합(별칭: commonjs).
  • amd(비동기 모듈 정의) — RequireJS와 같은 모듈 로더와 함께 사용됨.
  • umd(범용 모듈 정의) — amd, cjs, iife를 모두 하나로 작동함.
  • es – 번들을 ES 모듈 파일로 유지함. 다른 번들러에 적합하고 최신 브라우저에서 <script type=module> 태그로 포함(별칭: esm, module).
  • system – SystemJS 로더의 기본 형식(별칭: systemjs).
  • iife – <script> 태그로 포함하기에 적합한 자체 실행 함수. 애플리케이션에 번들을 만들려면 이것을 사용하는 것이 좋습니다.

이 글에서는 이러한 형식을 예를 들어 설명하겠습니다.

 

예제

Here is an example with four files to be bundled:

  • index.js
  • increase.js
  • decrease.js
  • others.js

The main entry file is index.js:

**
 * This is the main file
 */
import { increase } from './increase';
import { decrease } from './decrease';
import others, { e } from './others';

function multiply(total, value) {
  return total * value;
}

function divide(total, value) {
  return total / value;
}

export function power(total, value) {
  return total ** value;
}

let total = others.a;

total = increase(total, 10);
total = increase(total, 20);
total = decrease(total, 5);
total = multiply(total, e);

console.log(`Total is ${total}`);

4-6행에서 index.js는 increase.js, decrease.js, others.js에서 가져온 내용을 명시적으로 나열합니다.

increase.js는 다음과 같습니다.

/**
 * Increase the current total value
 * @param {number} total The current total value
 * @param {number} value The new value to be added
 * @returns {number} The new total value
 */
export const increase = (total, value) => total + value;

decrease.js는 다음과 같습니다.

**
 * Decrease the current total value
 * @param {number} total The current total value
 * @param {number} value The new value to be subtracted
 * @returns {number} The new total value
 */
export const decrease = (total, value) => total - value;

other.js는 다음과 같습니다.

xport default {
  a: 1,
  b: 2,
  c: () => 3,
};

export const d = 4;
export const e = 5;

이 예에서, others.js의 const d와 index.js의 divide() 함수는 사용되지 않습니다. index.js의 power() 함수도 사용되지 않지만 내보내집니다.

ES2015/ES6은 정적 분석기가 코드를 실행하지 않고도 종속성의 전체 트리를 빌드할 수 있도록 하는 정적 가져오기 및 내보내기를 도입했습니다. 또한, 이는 트리 셰이킹 최적화의 기초를 마련합니다. 위키피디아에 따르면:

Tree shaking eliminates unused functions from across the bundle by starting at the entry point and only including functions that may be executed.

트리 셰이킹은 진입 지점에서 시작하여 실행될 수 있는 함수만 포함함으로써 번들 전체에서 사용되지 않는 함수를 제거합니다.


이 예제는 특정 가져오기(import * 아님)를 사용하여 ES2015 스타일로 작성되었으므로 롤업 트리 흔들기 프로세스는 생성된 모든 형식에서 const d와 divide() 함수를 제거합니다. 내보낸 함수를 잠재적으로 사용할 수 있으므로 power() 함수는 유지됩니다.

 

CommonJS (CJS)

CJS는 웹 브라우저 외부의 Node 및 기타 생태계에 적합합니다. 서버 측에서 널리 사용됩니다. CJS는 ​​require() 함수와 module.exports를 사용하여 인식할 수 있습니다. require()는 다른 모듈에서 현재 범위로 심볼을 가져오는 데 사용할 수 있는 함수입니다. module.exports는 현재 모듈이 다른 모듈에서 필요할 때 반환하는 객체입니다.

CJS 모듈은 서버 개발을 염두에 두고 설계되었습니다. 자연스럽게 API는 동기식입니다. 즉, 모듈은 소스 파일 내에서 필요한 순서대로 현재 로드됩니다.

CJS는 동기식이며 브라우저에서 기본적으로 인식되지 않으므로 CJS 모듈은 트랜스파일러와 함께 패키징되지 않는 한 브라우저 측에서 사용할 수 없습니다. Babel 또는 Traceur와 같은 트랜스파일러는 이후 버전의 JavaScript에서 코드를 작성하는 데 도움이 되는 도구입니다. 환경이 이후 버전을 기본적으로 지원하지 않으면 트랜스파일러가 지원되는 버전으로 컴파일합니다.

다음은 CJS 형식의 Rollup 생성 파일입니다.

'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

/**
 * Increase the current total value
 * @param {number} total The current total value
 * @param {number} value The new value to be added
 * @returns {number} The new total value
 */
const increase = (total, value) => total + value;

/**
 * Decrease the current total value
 * @param {number} total The current total value
 * @param {number} value The new value to be subtracted
 * @returns {number} The new total value
 */
const decrease = (total, value) => total - value;

var others = {
  a: 1,
  b: 2,
  c: () => 3,
};
const e = 5;

/**
 * This is the main file
 */

function multiply(total, value) {
  return total * value;
}

function power(total, value) {
  return total ** value;
}

let total = others.a;

total = increase(total, 10);
total = increase(total, 20);
total = decrease(total, 5);
total = multiply(total, e);

console.log(`Total is ${total}`);

exports.power = power;

브라우저 측에서 이 파일을 실행하면 exports is not defined(라인 3)라는 메시지와 함께 오류가 발생합니다.

다음 코드를 index.html에 포함하면 오류를 해결할 수 있습니다.

<script>
  const exports = {};
</script>

 

Asynchronous Module Definition (AMD)

AMD는 비동기 모듈 로딩을 지원하기 위해 CJS에서 탄생했습니다. AMD와 CJS의 주요 차이점은 비동기 모듈 로딩 지원에 있습니다. AMD는 브라우저 측에서 작동하는 RequireJS에서 사용됩니다.

위키피디아에 따르면:

"AMD는 일부 CJS 상호 운용성을 제공합니다. 코드에서 유사한 내보내기 및 require() 인터페이스를 허용하지만 자체 define() 인터페이스가 더 기본적이고 선호됩니다."

다음은 AMD 형식의 Rollup 생성 파일입니다.

define(['exports'], function (exports) { 'use strict';

  /**
   * Increase the current total value
   * @param {number} total The current total value
   * @param {number} value The new value to be added
   * @returns {number} The new total value
   */
  const increase = (total, value) => total + value;

  /**
   * Decrease the current total value
   * @param {number} total The current total value
   * @param {number} value The new value to be subtracted
   * @returns {number} The new total value
   */
  const decrease = (total, value) => total - value;

  var others = {
    a: 1,
    b: 2,
    c: () => 3,
  };
  const e = 5;

  /**
   * This is the main file
   */
                                        
  function multiply(total, value) {
    return total * value;
  }

  function power(total, value) {
    return total ** value;
  }

  let total = others.a;

  total = increase(total, 10);
  total = increase(total, 20);
  total = decrease(total, 5);
  total = multiply(total, e);

  console.log(`Total is ${total}`);

  exports.power = power;

  Object.defineProperty(exports, '__esModule', { value: true });

});

브라우저 측에서 이 파일을 실행하면 define is not a function(라인 1)이라는 메시지와 함께 오류가 발생합니다.

index.html에 require.js를 포함하면 오류를 해결할 수 있습니다.

<script src=”https://requirejs.org/docs/release/2.3.6/minified/require.js"></script>

 

Universal Module Definition (UMD)

UMD는 서버 측과 브라우저 측에서 모든 곳에서 작동하도록 설계되었습니다. RequireJS와 같은 오늘날 가장 인기 있는 스크립트 로더와의 호환성을 제공하려고 시도합니다. 많은 경우 CJS 호환성을 처리하기 위해 특수 케이스를 추가한 AMD를 기반으로 사용합니다. 그러나 호환성으로 인해 읽고 쓰기가 복잡해지는 복잡성이 추가됩니다.

다음은 UMD 형식의 Rollup 생성 파일입니다.

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.example = {}));
}(this, (function (exports) { 'use strict';

  /**
   * Increase the current total value
   * @param {number} total The current total value
   * @param {number} value The new value to be added
   * @returns {number} The new total value
   */
  const increase = (total, value) => total + value;

  /**
   * Decrease the current total value
   * @param {number} total The current total value
   * @param {number} value The new value to be subtracted
   * @returns {number} The new total value
   */
  const decrease = (total, value) => total - value;

  var others = {
    a: 1,
    b: 2,
    c: () => 3,
  };
  const e = 5;

  /**
   * This is the main file
   */
                             
  function multiply(total, value) {
    return total * value;
  }

  function power(total, value) {
    return total ** value;
  }

  let total = others.a;

  total = increase(total, 10);
  total = increase(total, 20);
  total = decrease(total, 5);
  total = multiply(total, e);

  console.log(`Total is ${total}`);

  exports.power = power;

  Object.defineProperty(exports, '__esModule', { value: true });

})));

이 코드는 브라우저에서 작동합니다.

 

ES2015 Module (ESM)

ESM은 ES2015 이후 JavaScript에서 사용되는 공식 표준이 되었습니다. JavaScript 클라이언트 개발에서 널리 사용되고 있으며, 추가 유형이 있는 슈퍼셋인 TypeScript에서도 채택되었습니다.

^12.20.0 || ^14.13.1 || >=16.0.0 버전부터 Node에서 ESM을 지원하기 시작했습니다. ESM은 클라이언트와 서버 모두에서 사용되기 위해 인기를 얻고 있습니다.

정적 import 지시문을 사용하여 모듈을 현재 범위로 가져올 수 있습니다. 동적 import()는 ES2020부터 사용할 수 있습니다.

반면 export 지시문은 항목을 명시적으로 공개하는 데 사용할 수 있습니다.

다음은 ESM 형식의 Rollup 생성 파일입니다.

/**
 * Increase the current total value
 * @param {number} total The current total value
 * @param {number} value The new value to be added
 * @returns {number} The new total value
 */
const increase = (total, value) => total + value;

/**
 * Decrease the current total value
 * @param {number} total The current total value
 * @param {number} value The new value to be subtracted
 * @returns {number} The new total value
 */
const decrease = (total, value) => total - value;

var others = {
  a: 1,
  b: 2,
  c: () => 3,
};
const e = 5;

/**
 * This is the main file
 */

function multiply(total, value) {
  return total * value;
}

function power(total, value) {
  return total ** value;
}

let total = others.a;

total = increase(total, 10);
total = increase(total, 20);
total = decrease(total, 5);
total = multiply(total, e);

console.log(`Total is ${total}`);

export { power };

브라우저 측에서 이 파일을 실행하면 Uncaught SyntaxError: Unexpected token 'export' (45행)라는 오류 메시지가 표시됩니다.

index.html에서 스크립트 태그의 유형을 모듈로 설정하면 오류를 해결할 수 있습니다.

<script type=”module” src=”dist/bundle.js”></script>

 

System Module

SystemJS는 CJS, AMD, ESM 모듈을 지원하는 범용 모듈 로더입니다. Rollup은 코드를 SystemJS의 기본 형식으로 묶을 수 있습니다.

다음은 System 형식의 Rollup 생성 파일입니다.

System.register([], function (exports) {
  'use strict';
  return {
    execute: function () {

      exports('power', power);

      /**
       * Increase the current total value
       * @param {number} total The current total value
       * @param {number} value The new value to be added
       * @returns {number} The new total value
       */
      const increase = (total, value) => total + value;

      /**
       * Decrease the current total value
       * @param {number} total The current total value
       * @param {number} value The new value to be subtracted
       * @returns {number} The new total value
       */
      const decrease = (total, value) => total - value;

      var others = {
        a: 1,
        b: 2,
        c: () => 3,
      };
      const e = 5;

      /**
       * This is the main file
       */
      
      function multiply(total, value) {
        return total * value;
      }

      function power(total, value) {
        return total ** value;
      }

      let total = others.a;

      total = increase(total, 10);
      total = increase(total, 20);
      total = decrease(total, 5);
      total = multiply(total, e);

      console.log(`Total is ${total}`);

    }
  };
});

브라우저 측에서 이 파일을 실행하면 System is not defined(라인 1)라는 메시지와 함께 오류가 발생합니다.

system.js를 설치합니다.

npm install --save-dev systemjs

index.html에 system.js를 포함시키면 오류를 해결할 수 있습니다.

<script src="node_modules/systemjs/dist/s.min.js"></script>

 

Immediately Invoked Function Expression (IIFE) Module

모듈의 이름에서 알 수 있듯이 IIFE는 <script> 태그로 포함하기에 적합한 자체 실행 함수입니다. 이 형식을 사용하여 애플리케이션의 번들을 만들 수 있습니다. 변수 충돌을 피하고 코드를 비공개로 유지하기 위해 네임스페이스에 항목을 넣는 데 도움이 됩니다.

다음은 IIFE 형식의 Rollup 생성 파일입니다.

var example = (function (exports) {
  'use strict';

  /**
   * Increase the current total value
   * @param {number} total The current total value
   * @param {number} value The new value to be added
   * @returns {number} The new total value
   */
  const increase = (total, value) => total + value;

  /**
   * Decrease the current total value
   * @param {number} total The current total value
   * @param {number} value The new value to be subtracted
   * @returns {number} The new total value
   */
  const decrease = (total, value) => total - value;

  var others = {
    a: 1,
    b: 2,
    c: () => 3,
  };
  const e = 5;

  /**
   * This is the main file
   */

  function multiply(total, value) {
    return total * value;
  }
  
  function power(total, value) {
    return total ** value;
  }

  let total = others.a;

  total = increase(total, 10);
  total = increase(total, 20);
  total = decrease(total, 5);
  total = multiply(total, e);

  console.log(`Total is ${total}`);

  exports.power = power;

  Object.defineProperty(exports, '__esModule', { value: true });

  return exports;

}({}));

이 코드는 브라우저에서 작동합니다.

 

다양한 포맷으로 번들화시키기

rollup.config.js는 Rollup 구성 파일입니다. 선택 사항이지만 강력하고 편리하므로 권장됩니다.

다음은 여러 출력 형식을 한 번에 생성하는 데 사용한 출력 구성입니다.

output: [
  {
    file: 'dist/bundle.amd.js',
    format: 'amd',
    sourcemap: false,
  },
  {
    file: 'dist/bundle.cjs.js',
    format: 'cjs',
    sourcemap: false,
  },
  {
    file: 'dist/bundle.umd.js',
    format: 'umd',
    name: 'example',
    sourcemap: false,
  },
  {
    file: 'dist/bundle.es.js',
    format: 'es',
    sourcemap: false,
  },
  {
    file: 'dist/bundle.iife.js',
    format: 'iife',
    name: 'example',
    sourcemap: false,
  },
  {
    file: 'dist/bundle.system.js',
    format: 'system',
    sourcemap: false,
  },
]

 

결론

JavaScript 모듈 형식인 CJS, AMD, UMD, ESM, System, IIFE를 살펴보았습니다. 이러한 형식 중에서 ESM은 클라이언트와 서버 모두에 사용되는 인기를 얻고 있습니다.

읽어주셔서 감사합니다. 도움이 되었으면 좋겠습니다. 저의 다른 Medium 게시물은 여기에서 볼 수 있습니다.

https://jenniferfubook.medium.com/jennifer-fus-web-development-publications-1a887e4454af

 

Jennifer Fu’s Web Development Publications

List of Jennifer Fu’s web development publications on Medium…

jenniferfubook.medium.com

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기