티스토리 뷰

CommonJS is not going away : CommonJS는 사라지지 않습니다.

Some may be surprised to see the recent release notes for Bun mention CommonJS support. After all, CommonJS is a legacy module system, and the future of JavaScript is ES Modules (ESM), right? As a "forward-thinking" "next-gen" runtime, why would Bun put so much effort into improving CommonJS support?

 

최근 Bun의 릴리스 노트에 CommonJS 지원이 언급된 것을 보고 놀라신 분들도 계실 것입니다. 결국 CommonJS는 레거시 모듈 시스템이고 자바스크립트의 미래는 ES 모듈(ESM)이니까요. "미래 지향적인" "차세대" 런타임인 Bun이 CommonJS 지원 개선에 그토록 많은 노력을 기울인 이유는 무엇일까요?

 

Because CommonJS is here to stay, and that's okay! We think that better tooling can solve today's developer experience issues with CommonJS and ESM interop.

 

CommonJS는 계속 유지될 것이기 때문에 괜찮습니다! 더 나은 툴을 사용하면 CommonJS 및 ESM 상호 운용과 관련된 오늘날의 개발자 환경 문제를 해결할 수 있다고 생각합니다.

 

 


The situation, explained : 상황 설명

 

As you might imagine, it's often desirable to split your application into multiple files. When you do this, you need a way to reference code in other files.

 

상상할 수 있듯이 애플리케이션을 여러 파일로 분할하는 것이 바람직한 경우가 많습니다. 이 경우 다른 파일에 있는 코드를 참조할 수 있는 방법이 필요합니다.

The CommonJS module format was developed in 2009 and popularized by Node.js. Files can assign properties to a special variable called exports. Then, other files can reference properties from the exports object by "requiring" the file with a special require function.

CommonJS 모듈 형식은 2009년에 개발되어 Node.js에 의해 대중화되었습니다. 파일은 내보내기라는 특수 변수에 속성을 할당할 수 있습니다. 그런 다음 다른 파일은 특수 require 함수를 사용하여 파일을 "요구"함으로써 내보내기 객체의 속성을 참조할 수 있습니다.

 

// a.js
const b = require("./b.js");

b.sayHi(); // prints "hi"



// b.js
exports.sayHi = () => {
  console.log("hi");
};

 

To overly simplify how this works: when a file is required, the file is executed and the properties of the exports object are made available to the importer. CommonJS is designed for server-side JavaScript (in fact, it was originally named ServerJS), where it's expected that all files are available on the local filesystem. This is what it means for CommonJS to be synchronous — you can conceptualize require() as a "blocking" operation that reads the imported file and runs it, then hands control back to the importer.

 

작동 방식을 지나치게 단순화하자면, 파일이 필요할 때 파일이 실행되고 내보내기 객체의 속성을 가져오기가 사용할 수 있게 됩니다. CommonJS는 모든 파일을 로컬 파일 시스템에서 사용할 수 있어야 하는 서버 측 자바스크립트(사실 원래 이름은 ServerJS)를 위해 설계되었습니다. 이것이 바로 CommonJS가 동기식이라는 의미입니다. require()를 가져온 파일을 읽고 실행한 다음 다시 임포터에게 제어권을 넘기는 "차단" 작업으로 개념화할 수 있습니다.

 

ECMAScript modules were introduced in 2015 as part of ES6. An ES module declares its exports with the export keyword. The import keyword is used to import from other files. Unlike exports/require, both import and export statements can only occur at the top level of a file.

 

ECMAScript 모듈은 ES6의 일부로 2015년에 도입되었습니다. ES 모듈은 export 키워드를 사용하여 내보내기를 선언합니다. 가져오기 키워드는 다른 파일에서 가져오는 데 사용됩니다. 내보내기/요청과 달리 가져오기 및 내보내기 문은 모두 파일의 최상위 수준에서만 발생할 수 있습니다.

 

// a.js
import { sayHi } from "./b.js"

sayHi(); // prints "hi"


// b.js
export const sayHi = () => {
   console.log("hi");
};

 

Because ES modules are designed to work in browsers, it's expected that files are loaded over the network. This is what it means for ES modules to be asynchronous. Given an ES module, a browser can see what it imports and exports without running the file. Commonly, the entire module graph will be resolved (which may potentially involve multiple round-trip network requests) before any code is executed.

 

ES 모듈은 브라우저에서 작동하도록 설계되었기 때문에 파일이 네트워크를 통해 로드될 것으로 예상됩니다. 이것이 바로 ES 모듈이 비동기적이라는 의미입니다. ES 모듈이 주어지면 브라우저는 파일을 실행하지 않고도 가져오고 내보내는 내용을 확인할 수 있습니다. 일반적으로 코드가 실행되기 전에 전체 모듈 그래프가 해결됩니다(잠재적으로 여러 번의 왕복 네트워크 요청이 포함될 수 있음).

 

 


The case for CommonJS : CommonJS의 경우

CommonJS starts faster : CommonJS가 더 빠르게 시작됩니다.

 

ES modules are slower for larger applications. Unlike require, you either need load the entire module graph when using statements or await each import with expressions. For example, if you want to lazy-load a package for use in a function, your code must return a promise (which can introduce additional microticks and overhead).

 

ES 모듈은 대규모 애플리케이션의 경우 속도가 느립니다. require와 달리, 명령문을 사용할 때는 전체 모듈 그래프를 로드하거나 표현식을 사용하여 각 임포트를 기다려야 합니다. 예를 들어 함수에서 사용하기 위해 패키지를 지연 로드하려면 코드에서 프로미스를 반환해야 합니다(추가 마이크로틱과 오버헤드가 발생할 수 있음).

 

async function transpileEsm(code) {
  const { transform } = await import("@babel/core");
  // ... return must be a Promise
}

function transpileCjs(code) {
  const { transform } = require("@babel/core");
  // ... return is sync
}

 

ES Modules are slower by design. They need two passes in order to bind imports to exports. The entire module graph gets parsed and analyzed, then the code gets evaluated. This is split into distinct steps. It's what makes "live bindings" in ES Modules possible.

 

ES 모듈은 설계상 느립니다. 가져오기를 내보내기에 바인딩하려면 두 번의 패스가 필요합니다. 전체 모듈 그래프가 구문 분석되고 분석된 다음 코드가 평가됩니다. 이 과정은 여러 단계로 나뉩니다. 이것이 바로 ES 모듈의 "라이브 바인딩"을 가능하게 하는 요소입니다.

 

Consider these two simple files.

 

다음 두 개의 간단한 파일을 살펴보세요.
// babel.cjs
require("@babel/core")
// babel.mjs
import "@babel/core";

 

Babel is a package that consists of a huge number of files, so comparing the runtime of these two files is a decent way to evaluate performance costs associated with module resolution. The results:

 

바벨은 수많은 파일로 구성된 패키지이므로 이 두 파일의 런타임을 비교하는 것은 모듈 해상도와 관련된 성능 비용을 평가하는 데 적절한 방법입니다. 결과는 다음과 같습니다:

 

There's a difference of 85ms. In the context of serverless cold starts, that is massive. With Node.js the difference was 1.8x (~60ms).

 

85ms의 차이가 있습니다. 서버리스 콜드 스타트의 맥락에서 이는 엄청난 차이입니다. Node.js의 경우 그 차이는 1.8배(~60ms)였습니다.

 

 

Incremental loading : 증분 로딩

 

CommonJS allows for dynamic module loading—you can require() a file conditionally, or require() a dynamically constructed path/specifier, or require() in the body of a function. This flexibility can be advantageous in scenarios where dynamic loading is required, such as plugin systems or lazy-loading specific components based on user interactions.

ES modules provide a dynamic import() function with similar properties. In some sense, its existence is a testament to the fact that the CommonJS's dynamic approach has utility and is valued by developers.

 

조건부로 파일을 require()하거나 동적으로 구성된 경로/지정자를 require()하거나 함수 본문에 require()를 사용할 수 있는 등 CommonJS를 사용하면 동적 모듈 로딩이 가능합니다. 이러한 유연성은 플러그인 시스템이나 사용자 상호작용에 따라 특정 컴포넌트를 지연 로딩하는 등 동적 로딩이 필요한 시나리오에서 유용하게 사용할 수 있습니다.

ES 모듈은 비슷한 속성을 가진 동적 import() 함수를 제공합니다. 어떤 의미에서 이 함수의 존재는 CommonJS의 동적 접근 방식이 유용하고 개발자들에게 가치를 인정받고 있다는 사실을 입증하는 증거입니다.

 

It's already here : 이미 여기에 있습니다.

Millions of modules published to npm already use CommonJS. Many of these are both: (a) are no longer actively maintained, and (b) are critically important to existing projects. We will never hit a point where all packages can be expected to use ES modules. A runtime or framework that doesn't support CommonJS is leaving a huge amount of value on the table.

 

npm에 게시된 수백만 개의 모듈이 이미 CommonJS를 사용하고 있습니다. 이 중 상당수는 (a) 더 이상 활발하게 유지 관리되지 않거나 (b) 기존 프로젝트에 매우 중요한 모듈입니다. 모든 패키지가 ES 모듈을 사용할 것으로 기대할 수 있는 시점은 결코 오지 않을 것입니다. CommonJS를 지원하지 않는 런타임이나 프레임워크는 엄청난 가치를 잃게 됩니다.

 


CommonJS in Bun : Bun의 CommonJS

 

As of Bun v0.6.5, the Bun runtime natively implements CommonJS. Previously, Bun transpiled CommonJS files to a special "synchronous ESM" format.

 

Bun v0.6.5부터 Bun 런타임은 기본적으로 CommonJS를 구현합니다. 이전에는 Bun이 CommonJS 파일을 특수한 "동기식 ESM" 형식으로 트랜스파일했습니다.

 

 

Importing CommonJS from ESM : ESM에서 CommonJS 가져오기

You can import or require CommonJS modules from ESM modules.

ESM 모듈에서 CommonJS 모듈을 require 혹은 import해 올 수 있습니다.
import { stuff } from './my-commonjs.cjs';
import Stuff from './my-commonjs.cjs';
const myStuff = require('./my-commonjs.cjs');

This is a de-facto mechanism for a CommonJS module to indicate

(in conjunction with "type": "module" in package.json) that exports.default should be interpreted as the default export. When __esModule is set in a CommonJS module, a default import (import a from "./a.js") will import the exports.default property. Without the annotation, a default import will import the entire exports object.

 

이는 CommonJS 모듈이 (package.json의 "type": "module"과 함께) exports.default를 기본 내보내기로 해석해야 함을 표시하는 사실상의 메커니즘입니다. CommonJS 모듈에 __esModule이 설정되어 있으면 기본 가져오기("./a.js"에서 가져오기)를 통해 exports.default 속성을 가져오게 됩니다. 어노테이션이 없으면 기본 가져오기는 전체 내보내기 개체를 가져옵니다.

 

With the annotation:

// with __esModule: true
import mod, { foo } from "./module.js";
mod; // 5
foo; // "foo"

 

With the annotation:

// without __esModule
import mod, { foo } from "./module.js";
mod; // { default: 5 }
mod.default; // 5
foo; // "foo"

This is a de-facto standard way for a CommonJS module to indicate that exports.default should be interpreted as the default export.

 

이는 CommonJS 모듈이 exports.default를 기본 내보내기로 해석해야 함을 나타내는 사실상의 표준 방식입니다.

 


In summary : 요약

CommonJS is already here to stay. Not only that, it has real reasons to exist. We love ES modules here at Bun, but pragmatism is important. CommonJS is not a relic of a bygone era, and Bun treats it as a first-class citizen today.

 

CommonJS는 이미 존재하고 있습니다. 뿐만 아니라 존재해야 할 진짜 이유가 있습니다. 저희는 ES 모듈을 좋아하지만 실용주의가 중요합니다. CommonJS는 과거 시대의 유물이 아니며, Bun은 이를 오늘날의 일류 시민으로 취급합니다.

 

 

 

 

(원본 링크)

https://bun.sh/blog/commonjs-is-not-going-away

 

CommonJS is not going away | Bun Blog

We're hiring C/C++ and Zig engineers to build the future of JavaScript! Join our team → Some may be surprised to see the recent release notes for Bun mention CommonJS support. After all, CommonJS is a legacy module system, and the future of JavaScript is

bun.sh

 

'[개발] > JavaScript' 카테고리의 다른 글

[Queue] Microtask Queue, (Macro) Task Queue  (1) 2023.12.18
이벤트  (0) 2023.07.15
JavaScript SDK(JavaScript Software Development Kit)  (0) 2023.07.04
Arguments  (0) 2023.07.04
map VS. forEach  (0) 2023.07.04
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함