자바스크립트로 개발하다 보면 누구나 필수적으로 NPM을 사용하게 된다.
리액트, Next.js 같은 프레임워크/라이브러리 부터 작은 유틸 함수까지 누군가 만들어둔 패키지를 가져다 쓰면 훨씬 편하게 개발할 수 있다.
개발자로 일하면 "패키지는 어떻게 만들고, 어떻게 배포하는거지?"라는 궁금증이 항상 있었지만 일단 급하니까 그냥 가져다 쓰면서 넘어갔고 Webpack/Vite 같은 빌드 도구도 "그냥 쓰면 된다" 수준으로만 알고 있었다
이번에 애플에서 "Liquid Glass" 효과를 적극적으로 밀어주는것 같은데
CSS와 리액트 컴포넌트로 간단하게 구현해서 패키지로 배포해보면 좋을것 같다는 생각이들었다.
Liquid Glass 컴포넌트의 상세한 코드는 별도 게시글로 정리하고, 여기서는 NPM 패키지용 프로젝트 세팅부터 라이브러리 빌드, 배포까지 한 번에 정리해본다.
my-lib/
src/
components/LiquidGlass.tsx
hooks/useLiquidGlass.ts
index.ts # public API (re-exports)
.gitignore
package.json
rollup.config.js
tsconfig.json
tsconfig.build.json # 타입 전용 빌드 설정
README.md
LICENSE
💡 타입스크립트를 사용해서 리액트 컴포넌트를 개발할 것이기 때문에 당연히 이에 대한 설치는 완료되어 있어야 한다.
NPM에 업로드할 프로젝트이기 때문에 일반적으로 사용하던 React, Next.js 처럼 프로젝트를 만들면 안될것 같다는 생각이 들어서 AI를 활용해서 프로젝트 세팅을 어떻게 해야할지 물어본 결과
빌드도구와 번들러를 선택해야 한다는 것을 알았고 가장 추천하는 rollup을 사용하기로 결정했다.
먼저 빌드 도구란
개발자가 작성한 소스 코드를 실제 배포 가능한 형태로 변환하는 도구를 말한다.
쉽게 말해 브라우저 또는 Node.js에서 그대로 실행하지 못하는 소스(TypeScript, 최신 ES 문법, JSX, 모듈로 나뉜 코드, CSS등)를 실행/배포 가능한 형태로 바꾸는 전체 파이프라인을 "빌드"라고 하고
이걸 가능하게 해주는걸 빌드 도구라고 한다.
무엇을 개발하려고 하는지에 따라 빌드 도구 선택이 달라진다.
가벼운 라이브러리 개발 -> rollup
애를리케이션 개발(리액트 애플리케이션 같은) -> Webpack 또는 Vite
AI, 검색을 통해 빌드 도구에 대해서 알아본 결과
rollup은 라이브러리 개발에 최적화된 빌드 도구라고 소개된다.
그 이유로
1. Tree Shaking : 사용하지 않는 코드를 자동으로 제거해준다.
2. ES 모듈 우선 지원 :최신 JavaScript 표준 활용
3. 작은 번들 크기 :라이브러리용으로 최적화
4. 다양한 출력 방식 : ESM, CommonJS, UMD 모두 지원
다른 도구와 비교
따라서 간단한 라이브러리 개발이기 때문에 Rollup을 사용했다.
필요한 패키지를 다운로드
npm install -D rollup @rollup/plugin-typescript @rollup/plugin-node-resolve
@rollup/plugin-commonjs rollup-plugin-peer-deps-external rollup-plugin-postcss
rollup@rollup/plugin-typescript@rollup/plugin-node-resolveimport React from 'react' 같은 경로 해석@rollup/plugin-commonjsrequire() 형태의 모듈을 ES6 import로 변환rollup-plugin-peer-deps-externalrollup-plugin-postcss{
"name": "liquid-glass-mk",
"version": "1.1.4",
"type": "module",
"description": "A React library for beautiful liquid glass effects with customizable hover animations",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"files": [
"dist",
"README.md"
],
"scripts": {
"dev": "vite",
"build": "rollup -c",
"preview": "vite preview",
"prepublishOnly": "npm run build"
},
"keywords": [
"react",
"liquid-glass",
"glassmorphism",
"ui",
"effects",
"hover",
"animation",
"backdrop-filter",
"typescript"
],
"author": "",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/93minki/liquid-glass-mk.git"
},
"bugs": {
"url": "https://github.com/93minki/liquid-glass-mk/issues"
},
"homepage": "https://github.com/93minki/liquid-glass-mk#readme",
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
},
"devDependencies": {
...
},
"dependencies": {
...
}
}
name: npm 패키지 이름version: 패키지 버전description: 패키지 설명 (npm 검색 시 표시됨)keywords: 검색 키워드 배열 (npm에서 패키지를 찾을 때 사용)main: CommonJS 방식으로 import 할 때 사용되는 파일(dist/index.cjs.js)module: ES6 모듈 방식으로 import 할 때 사용되는 파일(dist/index.esm.js)types: TypeScript 타입 정의 파일 (dist/index.d.ts)files: npm에 업로드할 파일/폴더 목록 (dist 폴더와 README.md만 포함)type: “module”: ES6 모듈을 기본으로 사용한다는 선언peerDependencies: 이 패키지를 사용하는 프로젝트에서 반드시 설치해야 하는 패키지(React, React-DOM)devDependencies: 개발 시에만 필요한 패키지들 (빌드 도구, 타입 정의 등)repository: 소스코드가 있는 Git 저장소 정보bugs: 버그 리포트를 받을 URLhomepage: 패키지 홈페이지 URLprepublishOnly: npm publish 실행 전에 자동으로 실행되는 스크립트 (빌드 과정)import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import postcss from "rollup-plugin-postcss";
export default {
input: "src/index.ts",
output: [
{
file: "dist/index.cjs.js",
format: "cjs",
sourcemap: true,
},
{
file: "dist/index.esm.js",
format: "esm",
sourcemap: true,
},
],
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
typescript({ tsconfig: "./tsconfig.json" }),
postcss(),
],
};
input: “src/index.ts” : 빌드 진입점 파일. src/index.ts를 시작점으로 해서 모든 코드를 하나로 합친다.CJS (CommonJS): require() 방식으로 import 하는 구식 방법ESM (ES6 Module): import/export 방식으로 import 하는 현대적 방법package.json 파일 내용
”main”: “dist/index.cjs.js”,
”module”: “dist/index.esm.js”
번들러가 자동으로 선택함.
main 필드의 CJS 파일 사용module 필드의 ESM 파일 사용type: module 이 있으면 ESM, 없으면 CJSsourcemap?
.js.map 파일로 생성됨peerDepsExternal : React 같은 peerDependencies를 번들에 포함시키지 않음 (사용자가 직접 설치해야 함.)resolve : import React from ‘react’ 같은 경로를 실제 파일 위치로 해석commonjs: module.exports 방식을 export 방식으로 변환typescript : .ts 파일을 .js 파일로 컴파일하고 타입 체크postcss : CSS 파일을 처리하고 최적화rollup -c
-c = --configindex.cjs.js # CommonJS 방식 빌드 결과
index.esm.js # ES6 모듈 방식 빌드 결과
index.cjs.js : require() 방식으로 import 할 때 사용index.esm.js : import 방식으로 import 할 떄 사용index.cjs.js.map # CJS 파일의 소스맵
index.esm.js.map # ESM 파일의 소스맵
.map 파일들: 디버깅 시 원본 TypeScript 코드를 보여주기 위한 매핑 정보index.d.ts # 메인 타입 정의
App.d.ts # App 컴포넌트 타입
main.d.ts # main 파일 타입
components/LiquidGlass.d.ts # LiquidGlass 컴포넌트 타입
hooks/useLiquidGlass.d.ts # useLiquidGlass 훅 타입
utils/liquidGlassCursor.d.ts # 커서 유틸리티 타입
.d.ts 파일들: TypeScript 프로젝트에서 타입 정보를 제공import { LiquidGlass } from 'liquid-glass-mk 할 때 자동완성과 타입 체크 지원아이디가 없다면 npm 웹페이지에서 만들면 된다.
npm login
같은 이름의 패키지가 존재하는지 확인하는 용도
npm view liquid-glass-mk
만약 같은 이름의 패키지가 존재한다면 패키지 정보가 나오고 없으면 에러같은게 나오는데
만약 이미 존재하는 패키지 이름을 입력하면 패키지에 대한 내용이 나온다. 존재하는 패키지가 없을 경우 에러 메세지 같은게 나오는데, 에러 메세지가 나와야 내가 사용할 수 있는 패키지 이름이다.
npm run build
npm publish

npm run build를 실행하면 rollup으로 빌드가 실행되고 dist 폴더가 생성됨
그리고 npm publish를 실행하면 알아서 배포된다.
배포에 오랜 시간이 소요될줄 알았는데 빠르게 배포가 완료되었다 (거의 바로)
업데이트 내용이 발생하고 package.json에 있는 버전을 직접 수정하는 것보다
# 패치 버전 (1.0.0 -> 1.0.1)
npm version patch
# 마이너 버전 (1.1.0 -> 1.1.0)
npm version minor
# 메이저 버전 (1.0.0 -> 2.0.0)
npm version major
메이저, 마이너, 패치 버전중 어떤것을 올릴지 결정하고 아래 명령어를 실행하면 버전이 업데이트된다.
npm publish
게시글을 작성하기 1주일전에 패키지를 배포했는데
어느새 345번이나 다운로드 횟수가 발생했다.(누가 받은거지...)

CSS 틀려서 수정하고, Readme 업데이트 안해서 수정하고
수정 할때마다 패키지 반영을 위해 억지로 패치 버전을 올리면서...
역시 신중하게 테스트를 해보고 문제가 없을 때 배포를 해야하는구나를 느꼈다.. 1.1.4 이지만
사실 1.1.0이지 않을까...
그래도 내가 만든걸 누군가 써보고 있다는게 신기한 기분이고
재미있는 경험이었다
레포지토리에 관심있는 분들이라면 여기에서 확인하시면 됩니다.
https://github.com/93minki/liquid-glass-mk

클론하시고 로컬에서 실행하면 간단한 예시도 확인할 수 있습니다.