듀얼 패키지 NPM 모듈 구축 및 게시
Min-jun Kim
Dev Intern · Leapcell

소개
끊임없이 진화하는 JavaScript 생태계에서 재사용 가능한 코드를 공유하는 것은 효율적인 개발의 기본입니다. NPM 패키지는 개발자가 솔루션을 모듈화하고 더 넓은 커뮤니티에 기여할 수 있도록 함으로써 이러한 작업의 초석 역할을 합니다. 그러나 오랫동안 사용된 CommonJS(CJS) 표준과 함께 ECMAScript Modules(ESM)의 부상으로 두 환경 모두에 원활하게 통합되는 패키지를 작성하는 것은 중요해졌지만 때로는 어려운 작업이기도 합니다. 어느 한 표준을 무시하면 패키지 채택이 제한되고 사용자가 복잡한 해결 방법을 사용하도록 강요할 수 있습니다. 이 문서는 이러한 과정을 명확히 하고 ESM과 CJS를 모두 우아하게 지원하는 자체 NPM 패키지를 작성, 테스트 및 게시하는 방법에 대한 포괄적인 가이드를 제공하는 것을 목표로 합니다.
핵심 개념 이해
구현에 들어가기 전에 관련 핵심 개념에 대한 공통된 이해를 확립해 봅시다.
- CommonJS (CJS): Node.js에 의해 대중화된 이 모듈 시스템은
require()
를 사용하여 모듈을 가져오고module.exports
를 사용하여 내보냅니다. 동기식이며 수년 동안 서버 측 JavaScript의 기본값이었습니다.// CJS import const myModule = require('./myModule.js'); // CJS export module.exports = { myFunction: () => console.log('Hello from CJS!') };
- ECMAScript Modules (ESM): ES2015에 도입된 JavaScript의 공식 모듈 표준입니다.
import
및export
문을 사용하며, 이는 본질적으로 비동기적이며 브라우저 및 Node.js 환경 모두를 위해 설계되었습니다.// ESM import import { myFunction } from './myModule.js'; // ESM export export const myFunction = () => console.log('Hello from ESM!');
- Dual Package Hazard: 이는 패키지가 CJS 및 ESM 버전 모두를 제공하려고 할 때 발생하는 잠재적인 문제를 나타냅니다. 서로 다른 환경이 다른 모듈 유형을 해석하거나 상태가 복제되면 문제가 발생할 수 있습니다.
- Conditioned Exports:
package.json
의 강력한 기능으로, 환경(예: ESM의import
, CJS의require
, 웹의browser
)에 따라 다른 진입점을 정의할 수 있습니다. 이는 듀얼 패키지 위험을 해결하는 데 중요합니다.// package.json snippet for conditioned exports "exports": { ".": { "import": "./dist/esm/index.js", "require": "./dist/cjs/index.js" } }
package.json
의type
필드: 이 필드("type": "module"
또는"type": "commonjs"
)는 패키지 또는 범위 내의 모든.js
파일에 대한 기본 모듈 시스템을 정의합니다. Node.js가 파일을 해석하는 방식에 중요한 역할을 합니다.- Transpilation: 한 언어 또는 버전으로 작성된 소스 코드를 다른 언어 또는 버전으로 변환하는 프로세스입니다. JavaScript의 경우 일반적으로 Babel 또는 TypeScript와 같은 도구를 사용하여 더 넓은 호환성을 위해 새 구문(예: ESM
import/export
)을 이전 구문(예: CJSrequire/module.exports
)으로 변환하는 것을 의미합니다.
듀얼 패키지 모듈 구축
사용자가 CJS 및 ESM과 원활하게 작동하도록 보장하면서 사용자를 인사하는 간단한 유틸리티 패키지를 만드는 단계를 살펴봅시다.
1. 프로젝트 설정 및 초기화
먼저 패키지에 대한 새 디렉토리를 만들고 NPM 프로젝트를 초기화합니다.
mkdir my-greeting-package cd my-greeting-package npm init -y
2. 소스 코드
우리 패키지는 사용자에게 인사를 반환하는 단일 함수를 가질 것입니다. 최신 ESM 구문을 사용하여 작성합니다.
src/index.js
:
// src/index.js export function greet(name = 'World') { return `Hello, ${name}!`; }
3. 빌드 구성 및 트랜스파일링
CJS 및 ESM을 모두 지원하려면 소스 코드를 트랜스파일링해야 합니다. Rollup.js를 사용합니다. 이는 구성이 용이하고 라이브러리 번들링에 탁월하기 때문입니다.
npm install --save-dev rollup @rollup/plugin-terser @rollup/plugin-node-resolve
rollup.config.js
파일을 생성합니다.
// rollup.config.js import { terser } from '@rollup/plugin-terser'; import { nodeResolve } from '@rollup/plugin-node-resolve'; export default [ // CJS build { input: 'src/index.js', output: { file: 'dist/cjs/index.js', format: 'cjs', sourcemap: true, exports: 'named', // Ensures named exports are correctly handled for CJS }, plugins: [nodeResolve(), terser()], }, // ESM build { input: 'src/index.js', output: { file: 'dist/esm/index.js', format: 'esm', sourcemap: true, }, plugins: [nodeResolve(), terser()], }, ];
package.json
에 빌드 스크립트를 추가합니다.
// package.json snippet "scripts": { "build": "rollup -c", "test": "node test/test.js" // We'll add this later }, "main": "dist/cjs/index.js", // Fallback for older Node.js or tools "module": "dist/esm/index.js", // Hint for bundlers like Webpack/Rollup "type": "commonjs", // Default type for the package
빌드를 실행합니다.
npm run build
이렇게 하면 dist/cjs/index.js
와 dist/esm/index.js
가 생성됩니다.
4. 듀얼 패키지 지원을 위한 package.json
구성
이것이 가장 중요한 단계입니다. package.json
을 수정하여 조건부 내보내기를 사용하여 환경이 올바른 모듈 유형을 선택하도록 합니다.
// package.json { "name": "my-greeting-package", "version": "1.0.0", "description": "A simple package that greets the user.", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "type": "commonjs", "exports": { ".": { "import": "./dist/esm/index.js", "require": "./dist/cjs/index.js", "default": "./dist/cjs/index.js" }, "./package.json": "./package.json" }, "files": [ "dist" ], "scripts": { "build": "rollup -c", "test": "node test/test.js" }, "keywords": [ "greeting", "esm", "cjs" ], "author": "Your Name", "license": "MIT", "devDependencies": { "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", "rollup": "^4.12.0" } }
main
: CJS 환경(레거시 대체)에 대한 진입점을 지정합니다.module
: ESM 인식 번들러(예: Webpack, Rollup)에 대한 진입점을 지정합니다.type: "commonjs"
: 이렇게 하면 Node.js에 이 패키지의.js
파일이 기본적으로 CJS임을 알립니다..mjs
파일을 ESM용으로 사용했다면 이것이 필요 없거나 `