Vite와 tsup를 활용한 컴포넌트 라이브러리 게시 간소화
Emily Parker
Product Engineer · Leapcell

소개
빠르게 발전하는 프론트엔드 개발 세계에서 재사용 가능하고 공유 가능한 UI 컴포넌트를 만드는 것은 효율적인 개발과 일관된 사용자 경험 유지를 위해 매우 중요합니다. 컴포넌트 라이브러리는 이러한 모듈식 구조의 근간 역할을 하여 개발자가 기능과 스타일을 관리하기 쉬운 단위로 캡슐화할 수 있도록 합니다. 그러나 컴포넌트 라이브러리의 진정한 힘은 다른 사람들이 쉽게 소비할 수 있을 때 비로소 발휘되며, 이는 강력한 패키징과 npm과 같은 레지스트리로의 손쉬운 게시를 필요로 합니다. 전통적으로 컴포넌트 라이브러리를 위한 빌드 파이프라인을 설정하는 것은 복잡하고 시간이 많이 걸리는 작업이었으며, 종종 복잡한 Webpack 구성을 포함했습니다. 이 문서는 Vite 및 tsup와 같은 최신 도구가 이 프로세스를 어떻게 혁신하여 React 또는 Vue 컴포넌트 라이브러리를 npm에 빌드하고 게시하는 것을 훨씬 더 간단하고 빠르게 만들었는지 탐구합니다. 핵심 원칙, 실용적인 구현 및 프론트엔드 개발자에게 제공하는 실질적인 이점에 대해 자세히 살펴보겠습니다.
최신 컴포넌트 라이브러리 워크플로우
Vite와 tsup의 세부 사항으로 들어가기 전에 컴포넌트 라이브러리를 빌드하고 게시하는 데 관련된 주요 개념에 대한 일반적인 이해를 확립해 봅시다.
컴포넌트 라이브러리: React 또는 Vue와 같은 프레임워크로 작성된 사전 구축된 UI 컴포넌트 모음으로, 서로 다른 프로젝트에서 재사용할 수 있도록 설계되었습니다. 패키징 (번들링): 소스 코드(예: TSX, Vue SFC, CSS)를 다른 프로젝트에서 쉽게 소비할 수 있는 최적화된 브라우저 호환 파일(예: JavaScript, CSS)로 변환하는 프로세스입니다. 여기에는 일반적으로 트랜스파일링, 최소화 및 트리 쉐이킹이 포함됩니다. 모듈 형식: JavaScript 모듈을 구성하고 로드하기 위한 표준입니다. 최신 웹 개발에서 가장 일반적인 형식은 다음과 같습니다. * CommonJS (CJS): 주로 Node.js 환경에서 사용됩니다. * ECMAScript Modules (ESM): 브라우저와 Node.js 모두에 적합한 JavaScript 모듈의 공식 표준입니다. 타입 정의 (d.ts 파일): JavaScript 모듈에 대한 타입 정보를 제공하는 파일로, TypeScript 지원과 소비 프로젝트의 개발자 경험 향상에 중요합니다. npm (Node Package Manager): 개발자가 컴포넌트 라이브러리를 포함한 JavaScript 패키지를 게시하고 공유하는 세계 최대의 소프트웨어 레지스트리입니다.
과거에는 Webpack이 지배적인 번들러였으며, 엄청난 유연성을 제공했지만 종종 복잡한 구성과 느린 빌드 시간이라는 대가를 치렀습니다. "설정 불필요" 또는 "최소 설정" 번들러의 등장은 패키징 프로세스를 극적으로 단순화했습니다. Vite와 tsup는 이러한 차세대 도구의 대표적인 예로, 개발자 경험과 성능에 중점을 둡니다.
Vite를 사용한 컴포넌트 라이브러리 패키징 (React 예제)
Vite는 주로 매우 빠른 개발 서버로 알려져 있지만, 특히 최신 JavaScript 프레임워크의 빌드 도구로서도 뛰어납니다. 라이브러리 모드에 대한 구성은 놀랍도록 간단합니다.
간단한 React 컴포넌트 라이브러리 구조를 상상해 보겠습니다:
my-react-library/
├── src/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ └── index.ts
│ │ └── Card/
│ │ ├── Card.tsx
│ │ └── index.ts
│ └── index.ts // 메인 진입점
├── package.json
├── tsconfig.json
├── vite.config.ts
src/index.ts
는 컴포넌트를 다시 내보냅니다.
// src/index.ts export * from './components/Button'; export * from './components/Card';
이제 라이브러리 모드에 대한 vite.config.ts
를 구성해 보겠습니다.
// vite.config.ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { resolve } from 'path'; export default defineConfig({ plugins: [react()], build: { lib: { // 여러 진입점도 지원됩니다 entry: resolve(__dirname, 'src/index.ts'), name: 'MyReactLibrary', // UMD 빌드의 전역 변수 이름 fileName: (format) => `my-react-library.${format}.js`, }, rollupOptions: { // 라이브러리에 포함되서는 안 되는 외부 종속성 처리 external: ['react', 'react-dom'], output: { // UMD 빌드에서 외부 종속성에 사용할 전역 변수 제공 globals: { react: 'React', 'react-dom': 'ReactDOM', }, }, }, // tsc를 사용하여 타입 정의 생성 // TypeScript 프로젝트에서 활성화 // dts: true, // .d.ts 파일을 생성합니다 }, });
빌드를 위해 package.json
에 스크립트를 추가합니다.
// package.json { "name": "my-react-library", "version": "1.0.0", "main": "./dist/my-react-library.umd.cjs", "module": "./dist/my-react-library.es.js", "types": "./dist/index.d.ts", // TypeScript에 중요 "files": ["dist"], "scripts": { "build": "vite build && tsc --emitDeclarationOnly --declaration --outDir dist" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" }, "devDependencies": { "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "@vitejs/plugin-react": "^4.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", "typescript": "^5.0.0", "vite": "^4.0.0" } }
package.json
의 main
, module
, types
필드는 소비자가 올바른 모듈 형식과 타입 정의를 올바르게 가져오는 데 중요합니다. files
는 npm에 게시될 때 포함될 항목을 지정합니다. peerDependencies
는 라이브러리가 번들링되는 대신 소비 애플리케이션에서 제공하기를 기대하는 종속성을 선언합니다.
npm run build
를 실행하면 일반적으로 ES 모듈, CommonJS 및 UMD 형식을 포함하여 dist
폴더에 출력물이 생성되며, 타입 정의도 함께 제공됩니다.
tsup를 사용한 컴포넌트 라이브러리 패키징 (Vue 예제)
tsUp는 번들링을 단순화하는 또 다른 훌륭한 도구로, 특히 TypeScript 프로젝트에 적합합니다. Esbuild를 기반으로 하여 매우 빠르며 종종 최소한의 구성만 필요합니다.
Vue 컴포넌트 라이브러리를 고려해 봅시다.
my-vue-library/
├── src/
│ ├── components/
│ │ ├── MyButton.vue
│ │ ├── MyCard.vue
│ │ └── index.ts // 컴포넌트를 다시 내보냅니다
│ └── index.ts // 메인 진입점
├── package.json
├── tsconfig.json
├── tsup.config.ts
src/components/index.ts
는 다음과 같을 수 있습니다.
// src/components/index.ts import MyButton from './MyButton.vue'; import MyCard from './MyCard.vue'; export { MyButton, MyCard };
그리고 src/index.ts
는 단순히 다시 내보냅니다.
// src/index.ts export * from './components';
이제 tsup.config.ts
를 구성해 보겠습니다.
// tsup.config.ts import { defineConfig } from 'tsup'; import vue from '@vitejs/plugin-vue'; // 필요한 경우 다른 Vue 플러그인 export default defineConfig({ entry: ['src/index.ts'], format: ['cjs', 'esm'], // CommonJS 및 ES 모듈 dts: true, // 타입 정의 생성 clean: true, // 빌드 전에 dist 폴더 정리 splitting: false, // 단일 파일 컴포넌트 라이브러리의 경우 일반적으로 false sourcemap: true, external: ['vue'], // Vue는 피어 종속성입니다 esbuildPlugins: [ // .vue 파일을 처리하기 위해 Vite의 Vue 플러그인 사용 (직접 가져오는 경우) // 이것은 Vite 설정에서 더 일반적이지만, Vue 파일을 처리하는 방법을 보여줍니다 // 만약 컴파일된 JS/TS만 내보낸다면 tsup 자체에는 엄밀히 필요하지 않을 수 있습니다 // 하지만 소스 빌드에는 유용합니다. // tsup의 경우 .vue 파일을 JS 모듈로 미리 컴파일하거나 // 상위 단계에서 처리되도록 하는 것이 더 쉽습니다. ], // .vue 파일을 처리하기 위한 예제: JS 모듈로 미리 컴파일합니다. // 또는 tsup가 직접 TS를 처리하는 경우, esbuild 플러그인 확장이 필요합니다. // Vue 컴포넌트 라이브러리에 대한 일반적인 접근 방식은 // `vue-tsc`를 사용하여 .d.ts를 SFC용으로 생성하고, 그 다음 tsup를 사용하여 JS/TS를 번들링하는 것입니다. });
Vue 컴포넌트 라이브러리에 대한 보다 실용적인 tsup.config.ts
는 .vue
파일을 별도로 컴파일했거나 tsup
가 컴파일된 JS 모듈을 내보내는 .ts
파일만 소비하는 경우 더 간단할 수 있습니다. .vue
파일을 직접 처리하기를 원한다면 esbuild 플러그인이 필요하며, 이는 Vite만큼 간단하지 않습니다. 단순성과 견고성을 위해 Vite 빌드 (vite build --lib
)가 Vue SFC에 선호되는 경우가 많으며, vue-tsc
를 타입에 사용하고 then tsup
로 JS/TS를 번들링하는 방법을 사용할 수 있습니다.
혼합하여 사용하거나 Vue 컴포넌트가 이미 JS/TS 내보내기로 컴파일되었다고 가정하면:
// package.json { "name": "my-vue-library", "version": "1.0.0", "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "files": ["dist"], "scripts": { "build": "tsup", "dev": "tsup --watch" }, "peerDependencies": { "vue": ">=3.0.0" }, "devDependencies": { "@types/node": "^20.0.0", "tsup": "^8.0.0", "typescript": "^5.0.0", "vue": "^3.0.0" } }
npm run build
를 실행하면 dist/index.cjs
, dist/index.mjs
및 dist/index.d.ts
가 생성됩니다. tsup
는 타입 생성부터 다양한 출력 형식까지 많은 반복 작업을 자동으로 처리합니다.
npm으로 게시하기
라이브러리 빌드가 완료되면 npm에 게시하는 것은 간단한 프로세스입니다.
-
package.json
이 올바른지 확인합니다:name
: 패키지의 고유 이름입니다.version
: 시맨틱 버전 관리가 중요합니다 (예:1.0.0
).description
: 라이브러리에 대한 간략한 요약입니다.keywords
: 관련 검색어입니다.author
/license
: 중요한 메타데이터입니다.main
,module
,types
: 빌드된 출력 파일을 가리킵니다.files
: 포함될 파일, 일반적으로["dist"]
입니다.peerDependencies
: 외부 종속성을 올바르게 나열합니다.
-
npm에 로그인합니다:
npm login
npm 사용자 이름, 비밀번호 및 이메일을 입력하라는 메시지를 따릅니다.
-
게시합니다:
npm publish
범위가 지정된 패키지(예:
@your-scope/my-library
)를 게시하는 경우 다음을 사용할 수 있습니다.npm publish --access public
이 명령은 패키지를 npm 레지스트리에 업로드합니다.
-
업데이트 (필요한 경우): 이후 업데이트의 경우
package.json
에서version
을 증가시키고 (예:npm version patch
,npm version minor
또는npm version major
사용)npm publish
를 다시 실행합니다.
고급 고려 사항
- CSS/SCSS 처리: Vite와 tsup 모두 CSS 가져오기를 처리할 수 있습니다. 컴포넌트 라이브러리의 경우 원시 CSS 파일(예:
dist/css
폴더)을 내보내거나, 소비자가 스타일을 가져오는 방식에 따라 JavaScript 번들에 직접 컴파일하는 것을 원할 수 있습니다. Vite의 라이브러리 모드는 적절한 경우 자동으로 CSS를 추출합니다. tsup의 경우 esbuild 플러그인이나 별도 빌드 단계(예:.css
를 컴파일하는postcss
또는.scss
를 컴파일하는sass
)를 사용할 수 있습니다. - Storybook/문서: 컴포넌트 라이브러리를 구축하는 것은 종종 이를 문서화하는 것과 함께 진행됩니다. Storybook과 같은 도구를 사용하면 구성 요소를 독립적으로 개발, 테스트 및 문서화할 수 있으며, 궁극적으로 더 나은 유지 관리성과 채택으로 이어집니다.
- 테스트: 구성 요소의 안정성을 보장하기 위해 단위 및 통합 테스트를 구현합니다. Vitest (Vite 프로젝트용) 또는 Jest와 같은 도구가 훌륭한 선택입니다.
- Monorepo: 여러 관련 패키지(예: 공유 유틸리티 라이브러리 및 컴포넌트 라이브러리)가 있는 경우 Lerna 또는 Turborepo와 같은 도구를 사용하여 monorepo 설정을 고려하십시오.
Vite 또는 tsup를 사용하면 패키징 프로세스가 크게 단순화되어 개발자가 복잡한 빌드 구성보다는 구성 요소 논리 및 디자인에 집중할 수 있습니다. 속도와 사용 편의성은 최신 프론트엔드 라이브러리 개발에 이상적인 선택입니다.
결론
컴포넌트 라이브러리를 패키징하고 npm에 게시하는 것은 재사용성과 프론트엔드 개발 가속화를 촉진하는 중요한 단계입니다. Vite 및 tsup와 같은 최신 빌드 도구는 복잡한 기본 구성을 추상화하여, 특히 React 및 Vue 프로젝트에 대해 번개처럼 빠른 빌드 시간과 직관적인 구성을 제공합니다.
이러한 도구를 활용함으로써 개발자는 TypeScript 정의 및 다양한 모듈 형식을 갖춘 최적화되고 소비 가능한 패키지로 소스 코드를 효율적으로 변환하고 npm을 통해 개발자 커뮤니티와 쉽게 공유할 수 있습니다. 이러한 도구는 개발자가 탁월한 용이성으로 고품질의 성능 좋은 컴포넌트 라이브러리를 구축하고 배포할 수 있도록 지원합니다.