#NPM 라이브러리 개발과 배포
npm 라이브러리를 개발하고 배포하는 과정에 대한 이론적 내용을 정리해보았다. 라이브러리 개발에 필요한 핵심 개념과 고려사항을 알아보자.
#1. 모듈 시스템 고려하기
라이브러리를 배포할 때는 두 가지 주요 환경을 고려해야 한다:
- CommonJS (CJS): Node.js의 전통적인 모듈 시스템으로, require()를 사용한다.
- ES Modules (ESM): 모던 JavaScript의 표준 모듈 시스템으로, import/export 구문을 사용한다.
라이브러리의 모듈 시스템 지원 전략에는 몇 가지 접근법이 있다:
#1.1 듀얼 지원 접근법
{
// ...
"main": "./dist/library.umd.cjs", // CommonJS 환경용
"module": "./dist/library.js", // ESM 환경용
"exports": {
".": {
"import": "./dist/library.js", // ESM 임포트용
"require": "./dist/library.umd.cjs" // CJS 임포트용
}
}
// ...
}
#1.2 ESM 우선 접근법
최근 트렌드는 ESM을 우선 지원하는 것이다. 트리쉐이킹과 같은 최적화는 ESM에서만 제대로 작동하기 때문이다. type: "module" 속성을 추가하면 해당 패키지가 기본적으로 ES Modules를 사용함을 명시한다.
#1.3 전략 선택
- 범용 라이브러리: 두 환경 모두 지원하는 것이 좋다
- UI 컴포넌트/번들 크기가 중요한 라이브러리: ESM 우선 또는 ESM 전용이 유리하다
- Node.js 백엔드 도구: CommonJS 지원이 여전히 중요하다
#2. 타입 정의와 TypeScript
라이브러리 개발 시 타입 정의는 매우 중요하다. 두 가지 접근 방식이 있다:
-
JavaScript로 개발 + 별도의 타입 정의:
- .js 파일과 함께 .d.ts 파일을 수동으로 작성한다
- 복잡한 타입의 경우 유지보수가 어려울 수 있다
-
TypeScript로 개발 + 컴파일:
- TypeScript로 개발하고 JavaScript + 타입 정의로 컴파일한다
- 빌드 과정에서 자동으로 타입 정의 파일이 생성된다
- 타입 안정성이 높고 유지보수가 용이하다
#3. 빌드 도구 선택
라이브러리 빌드를 위한 다양한 도구가 있다:
- tsdx: TypeScript 라이브러리 개발을 위한 제로 설정 CLI이다
- Vite: 모던 빌드 도구로, 빠른 개발 환경과 최적화된 빌드를 제공한다
- Rollup: 라이브러리 번들링에 최적화된 도구이다
- tsup: esbuild 기반의 TypeScript 라이브러리 번들러이다
각 도구는 장단점이 있으며, 프로젝트 요구사항에 맞게 선택하는 것이 중요하다.
#4. 모노레포 구조
모노레포는 여러 패키지를 하나의 저장소에서 관리하는 방식이다. 라이브러리 개발에 특히 유용한 이유는:
- 라이브러리와 예제/데모 애플리케이션을 함께 관리할 수 있다
- 로컬에서 라이브러리 변경사항을 즉시 테스트할 수 있다
- npm에 배포하지 않고도 내부 패키지 간 의존성 관리가 가능하다
Yarn Workspaces나 npm Workspaces, 또는 Nx, Turborepo 같은 도구를 사용하여 구성할 수 있다.
#5. 테스트 환경 구축
라이브러리 품질 보장을 위한 테스트는 필수적이다:
- 단위 테스트: 각 기능의 정상 작동을 확인한다
- 통합 테스트: 여러 기능의 상호작용을 확인한다
- JSDOM: 브라우저 환경을 시뮬레이션하여 DOM 관련 테스트가 가능하다
Jest, Vitest, Mocha 등의 테스트 프레임워크를 사용할 수 있다.
#6. 문서화
라이브러리 사용자를 위한 문서는 매우 중요하다:
- README.md: 기본 사용법과 설치 방법을 안내한다
- API 문서: 모든 공개 API에 대한 상세 설명을 제공한다
- 예제: 일반적인 사용 사례에 대한 코드 예제를 제공한다
더 복잡한 문서화가 필요한 경우 전용 도구를 사용할 수 있다:
- VitePress: Vue 기반의 정적 사이트 생성기이다
- Docusaurus: React 기반의 문서 사이트 생성기이다
- Nextra: Next.js 기반의 문서화 도구이다
모노레포 구조에서는 이러한 문서 사이트를 별도의 패키지로 관리할 수 있다.
#7. 구현 방식 선택: 클래스 vs 함수
라이브러리 설계 시 구현 방식 선택은 중요하다:
-
클래스 기반:
- 상태와 메서드를 캡슐화하기 좋다
- 객체지향 패러다임에 익숙한 개발자에게 친숙하다
- 상속과 다형성 활용이 가능하다
-
함수 기반:
- 더 가볍고 간결한 API를 제공한다
- 함수형 프로그래밍 패러다임과 잘 어울린다
- 트리쉐이킹에 더 유리할 수 있다
#8. 트리쉐이킹 고려
트리쉐이킹은 사용하지 않는 코드를 제거하여 번들 크기를 줄이는 기술이다:
- ESM 사용: 트리쉐이킹은 ES Modules에서만 제대로 작동한다
- 개별 함수 내보내기: 큰 객체 대신 개별 함수를 내보내는 방식을 사용한다
#8.1 sideEffects 설정의 중요성
package.json에 "sideEffects": false 설정은 웹팩(Webpack)이나 다른 번들러의 트리쉐이킹 기능을 최적화하는 중요한 옵션이다:
{
"name": "my-library",
"version": "1.0.0",
"sideEffects": false
}
이 설정이 의미하는 바는:
-
사이드 이펙트 없음 선언: 이 패키지의 모든 파일이 사이드 이펙트가 없다고 선언한다. 여기서 '사이드 이펙트'란 모듈이 로드될 때 전역 상태를 변경하거나, 외부에 영향을 주는 코드를 의미한다.
-
트리쉐이킹 최적화: 번들러에게 "이 패키지의 어떤 파일도 단순히 임포트하는 것만으로는 애플리케이션에 영향을 주지 않으니, 실제로 사용되는 코드만 번들에 포함시켜도 된다"고 알린다.
-
예시로 이해하기:
// 만약 이 패키지에서 import { button } from "ui-library"; // button만 사용한다면, "sideEffects": false 설정이 있을 때 // modal, dropdown 등 다른 컴포넌트 코드는 최종 번들에 포함되지 않는다
주의해야 할 점:
-
패키지가 실제로 사이드 이펙트가 있는데 "sideEffects": false로 설정하면 문제가 발생할 수 있다. 특히 다음과 같은 경우 조심해야 한다:
- 전역 CSS 파일을 임포트하는 경우
- 전역 객체를 수정하는 경우 (예: window.X = Y)
- 폴리필을 적용하는 경우
-
특정 파일만 사이드 이펙트가 있다면 다음과 같이 설정할 수 있다:
"sideEffects": [ "*.css", "./src/polyfills.js" ]
#8.2 트리쉐이킹에 유리한 코드 구조
단순 함수를 리턴하는 모듈은 모든 메서드가 한꺼번에 불러와지므로, 메서드가 많을 경우 트리쉐이킹이 제대로 동작하지 않을 수 있다. 따라서 다음과 같은 방식을 고려할 수 있다:
// 트리쉐이킹에 불리한 방식
export const utils = {
method1,
method2,
method3,
};
// 트리쉐이킹에 유리한 방식
export const method1 = () => {
/* 구현 */
};
export const method2 = () => {
/* 구현 */
};
export const method3 = () => {
/* 구현 */
};
#9. 패키지 배포 준비
npm에 패키지를 배포하기 전에 확인해야 할 사항들:
- package.json 설정: 이름, 버전, 설명, 키워드, 라이센스 등을 정의한다
- 파일 선택: files 필드를 통해 배포할 파일을 지정한다
- gitignore/npmignore: 불필요한 파일을 제외한다
- README/LICENSE: 기본 문서화와 라이센스 정보를 제공한다
- 버전 관리: Semantic Versioning(SemVer)을 준수한다
#10. 최근 트렌드
npm 라이브러리 개발의 최근 트렌드는:
- ESM 우선: CommonJS만 지원하던 패키지들이 ESM을 우선 지원하는 추세이다
- TypeScript 채택 증가: 타입 안정성과 개발자 경험 향상을 위해 사용한다
- 번들 크기 최적화: 트리쉐이킹과 코드 분할을 통한 최적화를 중요시한다
- 모노레포 활용: 복잡한 라이브러리 생태계 관리를 위해 도입한다
일부 패키지는 트리쉐이킹의 이점을 극대화하기 위해 ESM만 제공하는 경우도 있다. 이는 번들 크기 최적화가 호환성보다 중요한 프론트엔드 라이브러리에서 특히 두드러진다.
#결론
npm 라이브러리 개발은 단순히 코드 작성 이상의 것을 요구한다. 모듈 시스템, 타입 정의, 빌드 도구, 테스트, 문서화, 패키지 설정 등 다양한 측면을 고려해야 한다.
특히 모듈 시스템과 트리쉐이킹 간의 균형을 찾는 것이 중요하다. 라이브러리의 용도와 타겟 사용자에 따라 듀얼 지원 전략이나 ESM 우선 전략 중 적절한 것을 선택해야 한다. 어떤 선택이든 그 결정과 이유를 문서에 명확히 기술하는 것이 사용자에게 도움이 된다.
이러한 이론적 기반을 잘 이해하면 더 품질 높은 라이브러리를 개발하고 배포할 수 있다.