[Javascript] 시나브로 자바스크립트 5주차 이모저모 - npm 라이브러리 개발과 배포

2025년 3월 14일

#Javascript#Inflearn#Sinabro#Npm#Package#Package.json#Node#Nodejs#Yarn#Yarn.lock#

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

라이브러리 개발 시 타입 정의는 매우 중요하다. 두 가지 접근 방식이 있다:

  1. JavaScript로 개발 + 별도의 타입 정의:

    • .js 파일과 함께 .d.ts 파일을 수동으로 작성한다
    • 복잡한 타입의 경우 유지보수가 어려울 수 있다
  2. 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
}

이 설정이 의미하는 바는:

  1. 사이드 이펙트 없음 선언: 이 패키지의 모든 파일이 사이드 이펙트가 없다고 선언한다. 여기서 '사이드 이펙트'란 모듈이 로드될 때 전역 상태를 변경하거나, 외부에 영향을 주는 코드를 의미한다.

  2. 트리쉐이킹 최적화: 번들러에게 "이 패키지의 어떤 파일도 단순히 임포트하는 것만으로는 애플리케이션에 영향을 주지 않으니, 실제로 사용되는 코드만 번들에 포함시켜도 된다"고 알린다.

  3. 예시로 이해하기:

    // 만약 이 패키지에서
    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 우선 전략 중 적절한 것을 선택해야 한다. 어떤 선택이든 그 결정과 이유를 문서에 명확히 기술하는 것이 사용자에게 도움이 된다.

이러한 이론적 기반을 잘 이해하면 더 품질 높은 라이브러리를 개발하고 배포할 수 있다.