안녕하세요. J4J입니다.
이번 포스팅은 react typescript 컴포넌트 라이브러리 배포하는 방법에 대해 적어보는 시간을 가져보려고 합니다.
관련 글
[React] Vite 사용하기
안녕하세요. J4J입니다. 이번 포스팅은 vite 사용하는 방법에 대해 적어보는 시간을 가져보려고 합니다. Vite란? vite는 webpack, rollup 등과 같이 모듈 번들링을 수행할 때 사용되는 번들러 중 하나입니
jforj.tistory.com
npm 패키지 배포, 처음부터 자동화까지 한 번에 정리
npm 패키지 배포, 처음부터 자동화까지 한 번에 정리
안녕하세요. J4J입니다. 이번 포스팅은 npm 패키지 배포하는 방법에 대해 적어보는 시간을 가져보려고 합니다. NPM 패키지 배포 npm 패키지 배포라고 하는 것은 한 번 만들어진 javascript 기반의 라이
jforj.tistory.com
Tsup
tsup은 typescript 형태로 구성되어 있는 프로젝트를 배포 가능한 형태로 구성하기 위한 esbuild 기반 번들러입니다.
react로 구성되어 있는 컴포넌트를 배포한다고 할 때 번들러로 고려해 볼 수 있는 것은 vite, webpack 등이 존재할 수 있습니다.
그럼에도 라이브러리 배포를 위해 사용하는 번들러로 tsup을 선택하는 이유는 다음과 같이 존재합니다.
- vite, webpack은 애플리케이션 서버 실행, build 등을 모두 포함한 애플리케이션 개발 중심의 번들러
- 그래서 라이브러리 배포에 불 필요한 기능들을 많이 소유하고 있음
- tsup은 라이브러리 패키징에만 초점을 맞춘 도구
- 더 가벼운 설정을 통해 esm, cjs 파일들을 지원, d.ts 파일 관리 제공
- 등등..
또한 tsup은 뭔가 typescript에서만 사용하는 것처럼 보일 수 있습니다.
하지만 typescript 기반의 컴포넌트가 아니라 javascript 기반의 컴포넌트여도 tsup을 선택하는 것도 좋은 선택지가 될 수 있습니다.
왜냐하면 d.ts 파일을 제공하는 것을 제외하고는 거의 동일한 설정들이 적용되기 때문입니다.
이 외에 tsup을 사용하더라도 vite와 같은 번들러들의 설정이 필요할 수도 있습니다.
다만, 꼭 사용해야 하는 상황이 발생되면 vite는 라이브러리의 배포를 위해 사용되는 설정은 모두 제거하고 tsup이 관리할 수 없는 별도의 설정만 담는 것을 권장합니다.
그래야 중복 설정이 발생하지 않고, 라이브러리의 배포는 모두 tsup에 의해서만 가볍게 관리를 할 수 있기 때문입니다.
마지막으로 tsup은 tsc의 사용과도 비교할 수 있습니다.
간단하게만 말해보면 tsc는 보통 type 검사 및 d.ts 파일을 생성하는 용도로 사용됩니다.
그래서 라이브러리 배포에 사용되는 esm, cjs 기반 파일들을 생성해야 하는 상황에서 tsc 만으로 프로젝트 환경을 구성할 수 없기에 tsup이 사용되어야 합니다.
다만, tsup은 코드 구조가 문제가 없다면 build 에러가 발생하지 않습니다.
타입 추론, type import 검증 등의 엄격한 타입 검증을 하기 위해서는 tsc를 활용한 타입 검사가 필요합니다.
최종적으로 tsc를 이용하여 타입 검사를 수행하고, tsup으로 패키징에 사용되는 파일들의 산출물을 제공하는 방식으로 많이 구성됩니다.
배포할 프로젝트 구성
배포 프로젝트는 결국 react 기반의 프로젝트 구성이 필요합니다.
그래서 가볍게 프로젝트 초기 구조를 만들기 위해 vite 템플릿을 이용하여 구성해 보려고 합니다.
[ 1. 프로젝트 생성 ]
$ npm create vite@latest { 프로젝트 명 } --template react-ts
[ 2. tsup 설치 ]
$ npm install -D tsup
[ 3. tsup 설정 파일 구성 ]
프로젝트 상위에 tsup.config.ts 파일을 생성한 뒤 다음과 같은 설정 정보들을 작성합니다.
// tsup.config.ts
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.tsx"], // build 대상 entry 파일 등록
format: ["esm", "cjs"], // esm, cjs 기반의 build 파일 생성
dts: true, // type 처리를 위한 dts 파일 생성
clean: true, // build 전 이전 build 결과물을 제거
external: ["react", "react-dom"], // bundle 결과물에 포함시키지 않을 모듈 (peer deps 제거)
outExtension({ format }) {
return { js: format === "esm" ? ".mjs" : ".cjs" }; // format 별 확장자 설정
}
});
[ 4. tsconfig 설정 구성 ]
tsup으로 dts 설정을 적용하려면 다음과 같은 tsconfig 설정들이 필요합니다.
// tsconfig.json
{
"compilerOptions": {
/* tsup을 이용한 dts 빌드 사용 시 설정 필요 */
"allowImportingTsExtensions": true,
"jsx": "react-jsx",
"module": "NodeNext",
"moduleResolution": "NodeNext"
},
}
[ 5. package.json 배포 설정 ]
react typescript 기반의 컴포넌트 배포를 위한 package.json 설정들을 다음과 같이 적용합니다.
해당 설정은 프로젝트 구조와 어떤 환경에서 배포하는지에 따라 구조가 변경될 수 있습니다.
// package.json
{
"name": "@jforj26/react-npm-deploy", // 배포되는 패키지 명
"version": "1.0.0", // 배포되는 버전
"description": "react + typescript + tsup을 이용한 npm deploy 테스트를 위한 패키지", // 배포되는 패키지 설명
"exports": {
".": {
"types": "./dist/index.d.ts", // 패키지의 type 파일
"import": "./dist/index.mjs", // 패키지를 import 할 때 사용되는 파일
"require": "./dist/index.cjs" // 패키지를 require 할 때 사용되는 파일
}
},
"types": "./dist/index.d.ts", // 일부 환경을 위한 type 파일 설정
"main": "./dist/index.cjs", // 일부 환경을 위한 cjs 기반 require 설정
"module": "./dist/index.mjs", // 일부 환경을 위한 esm 기반 import 파일 설정
"files": ["dist"], // 배포되는 파일/폴더
"keywords": [ // 패키지가 노출될 수 있는 keyword 구성
"button",
"text-field"
],
"author": "jforj", // 저작권자 설정
"license": "ISC", // 라이센스 설정
"publishConfig": { // public 기반으로 package 접근 가능하도록 설정
"access": "public"
},
"scripts": { // tsc + tsup을 이용한 build script 구성
"typecheck": "tsc --noEmit",
"build": "npm run typecheck && tsup"
},
"peerDependencies": { // react, react-dom은 peer deps 설정으로 변경
"react": ">=19",
"react-dom": ">=19"
}
}
[ 6. 배포 컴포넌트 구성 ]
tsup의 entry 설정으로 src/index.tsx 파일을 구성했기 때문에 모든 배포 컴포넌트는 해당 파일에 모두 담겨 있어야 합니다.
barrel 기반 구조로 파일들을 구성해 볼 때 다음과 같이 배포 컴포넌트를 작성할 수 있습니다.
// /src/buttons/button.tsx
import type { ButtonHTMLAttributes, ReactNode } from "react";
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children?: ReactNode;
color?: 'primary' | 'secondary'
}
export default function Button({ children, color = 'primary', ...props }: ButtonProps) {
const buttonStyle = {
'primary': {
backgroundColor: '#0F172A',
color: '#FFFFFF'
},
'secondary': {
backgroundColor: '#38BDF8',
color: '#000000'
}
}
return (
<button {...props} style={buttonStyle[color]}>
{children}
</button>
)
}
// /src/buttons/index.tsx
export { default as Button, type ButtonProps } from './Button';
// /src/text-fields/TextField.tsx
import type { InputHTMLAttributes } from "react";
export interface TextFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>{
size?: 'small' | 'large'
}
export default function TextField({ size = 'small', ...props }: TextFieldProps) {
const inputStyle = {
'small': {
width: '6rem',
height: '1.5rem'
},
'large': {
width: '8rem',
height: '2rem'
}
}
return (
<input type="text" {...props} style={inputStyle[size]} />
)
}
// /src/text-fields/index.tsx
export { default as TextField, type TextFieldProps } from './TextField';
// /src/index.tsx
export * from './buttons/index.tsx';
export * from './text-fields/index.tsx';
NPM 패키지 배포
패키지 배포하는 방법에 대해서는 이곳에 정리를 해놨습니다.
배포하는 방법을 모르시는 분들은 참고해 주시면 됩니다.
npm에 배포가 정상적으로 이루어졌다면 다른 패키지들을 설치하는 것과 동일하게 다음과 같이 설치해 볼 수 있습니다.
$ npm install { 패키지 명 }
패키지를 설치하게 되면 배포했던 컴포넌트들을 이렇게 사용할 수 있습니다.
import { Button } from '@jforj26/react-npm-deploy';
export default function App() {
return (
<div>
<Button onClick={() => console.log('button')}>button!</Button>
</div>
);
}
이상으로 react typescript 컴포넌트 라이브러리 배포하는 방법에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'SPA > React' 카테고리의 다른 글
| Tailwind로 만드는 React 컴포넌트 라이브러리 배포 가이드, tsup + tsc 활용 (0) | 2026.01.23 |
|---|---|
| Tailwind className 관리 가이드, clsx + twMerge + cva 역할 정리 (0) | 2026.01.18 |
| [React] SSE (Server-Sent Events) 사용하여 실시간 통신하기 (0) | 2024.05.08 |
| [React] Jest에서 호출되는 함수 mocking 하기 (0) | 2024.01.20 |
| [React] Jest에서 path alias 적용하기 (0) | 2024.01.18 |
댓글