JavaScript의 모듈 시스템은 크게 CommonJS(require)와 ESM(import) 방식으로 나뉩니다. 이 두 방식의 핵심적인 차이점 중 하나는 모듈 로딩 방식이며, require()는 동기적(Synchronous)이고, import는 비동기적(Asynchronous)으로 동작합니다.
이번 글에서는 두 방식의 실행 모델을 심층적으로 분석하여, 왜 require()는 동기적이고 import는 비동기적인지를 설명하겠습니다.
CommonJS의 require()는 호출 시점에서 해당 모듈을 즉시 로드하고 평가를 완료할 때까지 실행을 블로킹합니다. 따라서 코드 실행 흐름을 멈추고 모듈의 모든 내용을 읽고 실행한 후에야 다음 코드가 실행됩니다.
console.log("Before require");
const message = require("./module.js"); // <- 파일을 읽고 실행이 끝날 때까지 대기 (Blocking)
console.log("After require");
Before require
(module.js 실행 내용)
After require
✅ "Before require"가 출력된 후, require("./module.js")가 실행됩니다.
✅ module.js의 로딩과 실행이 완료될 때까지 블로킹되며, 그 이후 "After require"가 출력됩니다.
📌 require()는 동기적 실행 모델을 따르며, 모듈 로드가 완료될 때까지 실행 흐름을 멈춘다.
ESM(ECMAScript Modules)의 import는 비동기적으로 실행되며, 모듈을 병렬로 가져옵니다. 즉, import는 블로킹 없이 모듈을 로드하고, 이후 실행 흐름을 유지하면서 로드 완료 후 평가됩니다.
console.log("Before import");
import("./module.js").then(() => {
console.log("Module loaded");
});
console.log("After import");
Before import
After import
(module.js 실행 내용)
Module loaded
✅ "Before import"가 출력됨
✅ import("./module.js")는 비동기적으로 실행되므로 즉시 다음 코드("After import")가 실행됨
✅ module.js가 로드된 후 "Module loaded"가 출력됨
📌 import는 실행 흐름을 블로킹하지 않고, 모듈을 병렬로 가져오면서 필요할 때 평가된다.
| 비교 항목 | CommonJS (require()) | ESM (import) |
|---|---|---|
| 실행 방식 | 동기적 (Blocking) | 비동기적 (Non-blocking) |
| 모듈 로딩 시점 | 즉시 로드 | 필요할 때 로드 가능 (지연 로딩) |
| 브라우저 지원 | ❌ (Node.js 전용) | ✅ (브라우저 네이티브 지원) |
| Vite에서 사용 | ❌ 느리므로 비효율적 | ✅ 빠른 로딩 가능 |
✅ Vite가 ESM을 활용하는 이유
Vite는 require()처럼 모든 파일을 한 번에 로드하는 방식이 아니라, 브라우저의 ESM 기능을 활용하여 필요한 모듈만 비동기적으로 가져오기 때문에 빠른 개발 환경을 제공한다. 🚀
require()는 동기적(Synchronous) 실행 모델을 따르며, 모듈이 로드될 때까지 실행 흐름을 멈춘다.import는 비동기적(Asynchronous) 실행 모델을 따르며, 모듈이 로드되는 동안 다른 코드가 실행될 수 있다.고급 개발자로서 효율적인 모듈 로딩을 고려할 때, 어떤 환경에서 어떤 모듈 시스템을 사용할지 깊이 이해하는 것이 필수적입니다. 😊