들어가며
이전 편 서버 상태 관리 라이브러리 직접 만들기 - 1편에서는 서버 상태를 관리하는 useQuery훅을 만들었다. 이제 이 훅을 라이브러리로 만들어보자.
컴파일
현재 우리가 작성한 useQuery는 TypeScript 언어로 작성되어있다. 이 useQuery를 import해서 사용하는 프로젝트가 JavaScript 기반이라면 사용이 불가능할 것이다. 또한 TTypeScript기반이여도 import 후 다시 JavaScript로 컴파일을 해주어야하기 때문에 상당히 비효율적이다. 그래서 우리는 JavaScript로 컴파일을 할 것이다.
라이브러리는 rollup.js를 사용할 것이다. rollup.js는 컴파일 말고도 여러 라이브러리 포맷(CommonJS, ES modules, UMD 등)을 지원하고, 트리 쉐이킹으로 번들 크기도 줄여준다.
rollup.js 설치
npm install -D rollup
rollup.js의 필요한 플러그인 설치
rollup.js에 필요한 플러그인들을 설치해보자. 이 플러그인들은 TypeScript 코드를 컴파일하고, 다양한 모듈 형식을 지원하는 데 필요하다.
npm install -D @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-TypeScript @rollup/plugin-terser @rollup/plugin-babel @babel/preset-env @babel/preset-react
rollup.js 설정
// rollup.config.js
const resolve = require("@rollup/plugin-node-resolve");
const commonjs = require("@rollup/plugin-commonjs");
const TypeScript = require("@rollup/plugin-tTypeScript);
const terser = require("@rollup/plugin-terser");
const babel = require("@rollup/plugin-babel");
const packageJson = require("./package.json");
module.exports = {
input: "src/index.ts",
output: [
{
file: packageJson.main,
format: "cjs",
sourcemap: !isProduction,
},
{
file: packageJson.module,
format: "esm",
sourcemap: !isProduction,
},
],
plugins: [
resolve(),
commonjs(),
tyTypeScript tsconfig: "./tsconfig.json" }),
terser(),
babel({
babelHelpers: "bundled",
presets: ["@babel/preset-env", "@babel/preset-react"],
}),
],
external: ["react", "react-dom"],
};
rollup의 각 설정을 하나씩 살펴보자.
- input : 번들의 시작점
- output : 번들링 후 저장할 위치
- plugins
- @rollup/plugin-node-resolve : node_modules에 있는 서드 파티를 사용하기 위해 설정
- @rollup/plugin-commonjs : CommonJS 모듈을 ESM으로 바꾸어준다.
- @rollup/plugin-TypeScript : TypeScript Rollup과 통합한다.
- @rollup/plugin-terser : terser를 활용해 압축된 번들을 생성한다.
- @rollup/plugin-babel : Babel과 Rollup을 통합한다.
- @babel/preset-env, @babel/preset-react를 preset으로 설정한다.
tsconfig.json
우리가 작성한 TypeScript코드는 빌드 과정에서 JavaScript로 컴파일을 수행한다. 만약 TypeScript 프로젝트에서 JavaScript 코드의 타입을 알고 싶다면? 이 때 사용하는 파일이 바로 d.ts파일이다. d.ts파일은 JavaScript패키지의 타입만 모아둔 파일이다. 아래와 같이 tsconfig.json에서 설정해주면 된다.
// tsconfig.json
{
"declaration": true,
"declarationMap": true,
"declarationDir": "dist",
}
- declaration : .d.ts 파일을 생성한다. 이 파일은 TypeScript 인터페이스 정의를 포함하여 타입 정보를 제공한다.
- declarationMap : .d.ts.map 파일도 함께 생성되어 소스 코드와 선언 파일 간의 매핑을 지원한다.
- delcarationDir : .d.ts 파일들이 저장될 위치를 지정한다. 여기서는 "dist" 디렉토리에 저장되도록 했다.
빌드
rollup, TypeScript까지 설정을 마쳤으니 이제 빌드를 해보자.
// package.json
{
// ..
"scripts": {
"build": "rollup -c",
// ..
}
// ..
}
npm run build 명령어를 입력하면 아래와 같이 dist폴더에 .cjs, .mjs, .d.ts, .d.ts.map 파일이 생성이 된다.
package.json 설정
빌드한 이 패키지를 다른 프로젝트에서 사용하기 위해서는 package.json에 어떤 폴더를 찾아야할지 명시해주어야한다. 우리가 패키지를 저장한 src폴더가 아니라, 패키지를 빌드한 dist폴더를 가리키도록 아래와 깉이 지정해주어야한다.
// package.json
{
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
// ..
}
- main : 기본 진입점
- Node.js의 기본 설정인 CommonJS 모듈 시스템의 파일인 .cjs를 매칭해주었다. CommonJS 모듈시스템의 프로젝트에서 이 패키지를 설치하면 해당 파일에서 원하는 파일을 탐색할 것이다.
- module : ES 모듈 시스템의 패키지 진입점
- types : d.ts파일의 경로
exports 필드를 사용해 더 상세한 파일 경로를 지정할 수 있지만, 아직은 외부 패키지에 제공할 경로가 하나이기 때문에 따로 사용하지 않았다. 만약 exports 필드를 사용한다면 다음과 같이 작성할 수 있을 것이다.
// package.json
{
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
}
// ..
}
배포
npm publish
전체 코드
전체 코드는 아래에서 확인할 수 있다.
그리고 배포한 라이브러리는 다음과 같다.
https://www.npmjs.com/package/@guesung/query
맺으며
Rollup 설정을 하고 배포하기까지 많은 어려움이 있었지만, 이렇게 글로 정리하고 보니 별것 아닌 것처럼 보인다. 하지만 이 코드들에는 플러그인과 설정을 구성하고, 빌드 후 실제 프로젝트에 설치해 사용하면서 발생한 에러들을 해결해 나간 노력이 고스란히 담겨 있다.
요즘에는 빌드 도구가 참 많다. Rollup 외에도 tsup, tsx, tsdown, esbuild 등이 있다. 특히 tsup은 esbuild 기반으로 만들어진 빌드 도구인데 설정이 간단하다고 한다. 이렇게 많은 빌드 도구가 있어 처음에는 "이거 다 써봐야 하는 거 아니야?"라는 막연한 두려움이 있었다. 하지만 빌드 과정은 결국 다 비슷하고, 설정도 비슷하다. 단지 얼마나 간소화되어 있느냐의 차이일 뿐이다. 그러니 모든 라이브러리를 쓸 줄 알기보다는 하나의 라이브러리를 쓰더라도 동작이 잘되도록 잘 쓰는 것이 중요하다는 것을 깨달았다. 그리고 현재 라이브러리에서 느낀 불편함을 다른 라이브러리에서 해결해주고 있다면 그때 가서 마이그레이션을 고민해보는 것도 적절할 것이다. 현재 나는 Rollup의 많은 설정으로 인해 tsup으로 마이그레이션할지 고민 중이다.
생각해보면 고민할 수 있다는 건 참 좋은 일이다. 상황과 입맛에 따라 골라서 사용할 수 있으니까. 앞으로도 하나의 기술에 편향되지 않고 다양한 기술을 볼 수 있는 개발자가 되어야겠다고 생각했다.