핵심 정리
// math.js — CommonJS 모듈 정의
function add(a, b) { return a + b; }
function multiply(a, b) { return a * b; }
module.exports = { add, multiply };
// index.js — 불러오기 (같은 파일을 두 번 require해도 캐시에서 반환)
const math1 = require("./math");
const math2 = require("./math"); // 동일한 객체 참조
console.log(math1 === math2); // true
console.log(math1.add(2, 3)); // 5CommonJS 기본형
CommonJS 기준으로 먼저 눈에 들어와야 하는 기본형은 아래입니다.
- 여러 값 내보내기:
module.exports = { ... } - 하나만 내보내기:
module.exports = value - 로컬 파일 가져오기:
require("./file") - 코어 모듈 가져오기:
require("node:fs") - 패키지 가져오기:
require("pkg")
require()는 "파일을 실행하고 결과를 캐시한다"
CommonJS를 다시 볼 때 가장 중요한 문장은 이것입니다. 같은 파일을 다시 require()해도 새 인스턴스를 만들지 않고, 첫 실행 결과를 돌려줍니다. 설정 로더, 싱글턴, 연결 객체가 왜 공유되는지도 여기서 설명됩니다.
module.exports가 진짜 반환값이다
exports는 편의용 별칭일 뿐입니다. 속성 추가는 가능하지만, 통째로 재할당하면 연결이 끊깁니다. "왜 import했는데 빈 객체가 나오지?" 같은 문제는 여기서 많이 납니다.
exports = { add }; // 연결 끊김
module.exports = { add }; // 실제 반환값 변경해석 순서는 생각보다 단순하다
- 내장 모듈
- 패키지 이름이면
node_modules ./,../가 붙으면 파일 경로
Node 기본 모듈 카드에서는 복잡한 ESM 조건부 로딩보다 이 흐름만 먼저 잡는 편이 좋습니다.
순환 의존성은 기술보다 설계 문제다
순환 require()는 부분 초기화 객체를 돌려줄 수 있습니다. 이걸 우회하는 트릭보다, 공유 타입/상수/유틸을 별도 모듈로 분리하는 쪽이 보통 맞습니다.
언제 어떤 방식으로 내보낼까
체크포인트
- 여러 값 내보내기:
module.exports = { ... } - 하나만 내보내기:
module.exports = value exports는 별칭이지 반환값 자체가 아님- 같은 모듈 재호출: 캐시된 객체 공유
- 순환 의존성: 구조 재검토 우선
주의할 점
전체 반환값을 바꾸려면 exports = ... 대신 module.exports = ...를 써야 한다. exports.foo = ...처럼 속성을 붙이는 용도는 괜찮지만, exports 자체를 재할당하면 module.exports와 연결이 끊긴다. CommonJS 문법이 헷갈리면 module.exports만 쓰는 쪽이 더 안전하다.
참고 링크
3 sources