Rollup.js - 플러그인으로 완성도를 높이다
지난 포스팅에서 rollup.js 를 이용해 두 개의 자바스크립트 파일을 하나로 묶고, rollup.config.js 파일을 구성해서 CLI가 아닌 스크립트로 설정 파일을 관리하는 것 까지 진행했습니다.
이번 시간에는 rollup 에 날개를 달아줄 플러그인들을 살펴보고 나아가 요즘 핫한 typescript 까지 적용해보도록 하겠습니다.
들어가기 전에
사실 이전에 구성했던 프로젝트가 번들링이 잘 되고는 있었지만, 빌드 시에 경고 메세지가 출력되고 있었습니다.
$ yarn build
(!) Unresolved dependencies
https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency
faker (imported by src/faker.js)
해석하자면 우리의 src/faker.js 파일에서 가져온 faker 모듈에 대한 처리가 잘 되지 않았다는 얘기네요.
지난 포스팅에 Tree shaking에 대해 언급했었는데, main.js 에서 다른 모듈을 가져올 때 실제로 사용되는 함수만 번들링의 결과물에 포함시킨다는 내용이었습니다.
여기서 추가적으로 rollup은 우리가 직접 작성한 모듈 말고, relative path를 가리키는 외부 모듈을 가져올 때 우리의 번들링 결과물에 포함시키지 않고, 단순히 가리키기만 합니다.
즉 우리가 만든 이 프로젝트를 누군가 가져다 쓸 때는 faker 라는 모듈 없이 작동이 안된다는 뜻입니다. 당연한 얘기죠.
사실 이 부분은 우리가 만든 패키지를 npm에 배포할 때 faker 모듈을 devDepenency가 아닌 depenency에 설치해두면 가져다 쓰는 사람이 우리 패키지를 설치할 때 직접 yarn add faker 를 하지 않더라도 알아서 같이 node_modules 안에 설치가 됩니다.
결과적으로 현재 발생하는 경고 메세지를 무시해도 괜찮지만, 공식 문서에는 이 부분이 의도적이더라도 명시해주면 좋겠다고 기술하고 있습니다.
경고 메세지를 없애는 방법은 간단합니다.
export default {
input: 'src/main.js',
output: {
dir: 'dist',
format: 'cjs'
}
external: [ 'faker' ]
};
외부 모듈에 대해 직접 이름을 명시하면 더 이상 오류 메세지는 출력되지 않습니다.
외부 모듈도 포함시키고 싶다면?
어떻게 보면 우리 프로젝트에서는 faker 의 faker.name.findName() 함수를 제외하면 아무것도 사용하지 않기 때문에 우리 모듈을 가져다 쓰는 사람이 faker 모듈을 전부 설치하는 게 좀 낭비같기도 합니다. 현재 faker는 41MB나 되거든요.
그렇다면 외부 모듈에 대해서도 Tree shaking을 이용하면 크기를 많이 줄일 수 있겠다는 생각이 드네요.
고맙게도 그런 역할을 하는 플러그인이 제공되고 있어서 아주 쉽게 구현할 수 있습니다.
$ yarn add -D @rollup/plugin-node-resolve
프로젝트에 플러그인 역할을 할 패키지를 설치 후, 설정 파일을 수정합시다.
import { nodeResolve } from "@rollup/plugin-node-resolve";
export default {
input: "src/main.js",
output: {
dir: "dist",
format: "cjs",
},
plugins: [nodeResolve()],
};
이렇게 설정하고 번들링을 하면 예상되는 결과는 우리의 결과물에 faker의 findName() 함수에 대한 코드가 포함되어 있어야겠죠.
$ yarn build
[!] Error: 'default' is not exported by node_modules/faker/index.js, imported by src/faker.js
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
src/faker.js (1:7)
1: import faker from 'faker'
^
오류가 발생했습니다. 이번엔 번들링 자체가 되지 않고 있습니다..
문서에서는 이러한 문제는 CommonJS 로 작성된 모듈들을 번들링 결과물에 포함시키려고 할 때 문제가 발생한다고 설명합니다.
당연하게도 우리가 어떤 파일에서 import a from './a.js' 라고 가져올 수 있는 건, a.js 파일에는 export default 이 있기 때문에 가능한 시나리오입니다.
하지만 faker 모듈을 살펴보면 그런 부분이 없습니다. 사실 faker 뿐만 아니라 엄청나게 많은 모듈들이 faker 와 같은 포맷으로 작성되어 있습니다.
그래서 여기서 또 다른 플러그인 하나가 등장합니다. CommonJS 로 작성된 모듈들을 ES6 바꾸어서 rollup이 해석할 수 있게 도와줍니다.
$ yarn add -D @rollup/plugin-commonjs
패키지를 설치하고 설정 파일을 다시 수정합시다.
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
export default {
input: "src/main.js",
output: {
dir: "dist",
format: "cjs",
},
plugins: [nodeResolve(), commonjs()],
};
다시 빌드 해봅시다.
$ yarn build
created dist in 5.2s
✨ Done in 5.84s.
빌드 타임은 좀 많이 늘어났습니다. 그래도 빠른 편입니다.
번들링 결과물이 길어서 첨부하지는 못하지만, faker 의 코드들이 번들링 결과물에 추가되어있는 걸 볼 수 있습니다. 아마 faker 내부에서 findName() 을 구현할 때 많은 단계를 거치는 것 같습니다.
그래도 우리의 main.js 파일의 크기는 현재 1.8MB입니다. 우리 코드를 제외하더라도 거의 40MB가량 이득을 봤습니다.
심지어 이 파일만 바깥으로 빼서 node main.js 로 실행시켜보면 다른 node_modules 이 없더라도 잘 실행됩니다. 필요한 부분이 전부 파일 안에 포함되었으니까요. 의존성이 없어지면서 파일 크기도 엄청나게 줄였습니다.
이제 사용자가 불필요하게 faker 모듈을 설치하지 않아도 되겠습니다.
타입스크립트
이제는 타입스크립트가 거의 대세가 된 것 같습니다. 저 역시 타입스크립트를 도입하고 그 필요성을 절실히 느끼고 있기 때문에, typescript로 작성되어있지 않은 패키지를 설치할 때는 조금 꺼려지게 되더군요.
먼저 우리 프로젝트에서 typescript를 사용하기 위해 패키지를 설치하도록 합시다.
$ yarn add -D typescript tslib
tslib 의 경우 rollup 이 typescript를 번들링할 때 필요해서 같이 설치해야합니다.
타입스크립트를 사용하기 위해 tsconfig.json 파일을 만들어줍시다. 만드는 방법은 간단합니다. 타입스크립트를 설치하면 사용 가능한 CLI인 tsc 명령어를 이용하면 템플릿을 자동으로 만들 수 있습니다.
$ node_modules/.bin/tsc --init
이번에도 역시 typescript를 전역으로 설치하지 않고 로컬에 설치했기 때문에, CLI를 사용하고 싶다면 프로젝트 루트 디렉토리에서 node_modules에 들어있는 tsc 를 직접 사용하면 되겠습니다.
{
"compilerOptions": {
"target": "es5",
"module": "CommonJS",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
저는 이정도로 간단하게만 사용하겠습니다.
타입스크립트를 사용하기 위한 준비가 되었으니 main.js 와 faker.js 의 확장자를 .ts로 바꿔주시면 되겠습니다.
이전에 js로 작성했던 파일의 확장자를 ts로 바꾸어도 변경할 부분은 없을 것 같습니다.
이제 우리 프로젝트의 구조는 이러합니다.
project/
|- node_modules
|- src
|- faker.ts
|- main.ts
|- package.json
|- rollup.config.js
|- tsconfig.json
|- yarn.lock
이제 rollup 이 typescript 파일을 읽어서 번들링을 할 수 있도록 도와주는 패키지를 설치합시다.
$ yarn add -D @rollup/plugin-typescript
다음 설정 파일을 수정합시다.
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
export default {
input: "src/main.js",
output: {
dir: "dist",
format: "cjs",
},
plugins: [
nodeResolve(),
commonjs({ extensions: [".js", ".ts"] }),
typescript(),
],
};
플러그인에 typescript 를 추가하고, commonjs가 .ts 파일도 읽어들일 수 있도록 설정합니다.
이 typescript() 플러그인 안에 타입스크립트에 대한 옵션을 넣어줄 수도 있지만, 없다면 자동으로 프로젝트 루트 디렉토리에 있는 tsconfig.json 파일을 찾아서 동기화를 해줍니다. 정말 간편하네요.
$ yarn build
(!) Entry module "src/main.ts" is implicitly using "default" export mode, which means for CommonJS output that its default export is assigned to "module.exports". ... 이하 생략
created dist in 5.6s
✨ Done in 6.49s.
정상적으로 빌드가 되었습니다. 빌드 타임은 약간 더 길어졌지만, 결과물은 기존과 같습니다.
다만 새로운 경고 메세지가 등장했습니다.
기본적으로 rollup 은 우리의 모듈을 번들링할 때 단 하나의 객체를 내보내는 export default 인지, 아니면 default 없이 일일이 이름을 지어서 내보내는지 추측해서 번들링 하도록 되어있습니다.
output.exports 의 옵션으로 default, named 아니면 none 3가지 옵션을 주고 있습니다.
rollup 의 권장사항은 named 입니다. 애초에 코드를 작성할 때도 마지막에 export default 를 하지 않기를 권장합니다.
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
export default {
input: "src/main.js",
output: {
dir: "dist",
format: "cjs",
exports: "named",
},
plugins: [
nodeResolve(),
commonjs({ extensions: [".js", ".ts"] }),
typescript(),
],
};
exports 를 추가하면 더 이상 경고 메시지는 나오지 않습니다.
파일을 더 압축해보자
사실 지금 번들링의 결과물은 불필요한 공백이 파일 크기를 많이 잡아먹고 있습니다. 이를 모두 제거해버립시다.
$ yarn add -D rollup-plugin-terser
다음은 설정 파일을 수정합시다.
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import { terser } from "rollup-plugin-terser";
export default {
input: "src/main.js",
output: {
dir: "dist",
format: "cjs",
exports: "named",
},
plugins: [
nodeResolve(),
commonjs({ extensions: [".js", ".ts"] }),
typescript(),
terser(),
],
};
빌드 후 파일을 열어보면 공백이 모두 제거 되었고, 파일이 1.8MB에서 1.3MB까지 줄어들었습니다.
마무리
이상으로 Rollup 번들러를 이용해서 간단하게 번들링에 대해서 알아보았습니다.
사실 오픈 소스 프로젝트처럼 다른 사람들을 위해 라이브러리를 만드는 것이 아니라면, 번들링 하는 방법을 직접적으로 알 필요는 없습니다. 이미 대부분의 메이저 프레임워크들은 내부적으로 잘 구현이 되어있기 때문입니다.
하지만 내가 만든 툴을 npm이나 CDN 같이 사람들에게 툴로써 제공하고 싶다면, 번들링은 선택이 아닌 필수라고 생각합니다.
같은 카테고리의 다른 글
[Nuxt 3] Composition API로 자동 스크롤링 기능 구현하기
이번 포스팅에서는 실시간 채팅 서비스에서 새로운 대화 내용이 추가되었을 때 자동으로 스크롤이 계속해서 아래로 내려가면서, 스크롤을 조작함에 따라 자동 스크롤이 활성화/비활성화되는 기능을 Vue 3에서 새로 추가된 Composition API를 통해 만들어볼겁니다.
Twilio 번호 구매 없이 연락처 인증 서비스 5분만에 구현하기
이번 포스팅에선 Twilio를 이용해 Node.js에서 개인 번호를 발급받지 않고, 핸드폰 번호 인증을 매우 간단하게 구현하는 방법에 대해 소개해드리겠습니다.
[Nuxt 3] 사이드 프로젝트 만들기 - 개발 환경 설정편
저번 사이드 프로젝트 만들기 - 기획편의 다음 편입니다. 이번엔 nuxt3의 주요 변경사항 일부를 알아보고, 쾌적한 개발 환경을 위해 몇 가지 세팅을 해보도록 하겠습니다.
[Nuxt 3] 사이드 프로젝트 만들기 - 기획편
올해 첫 개발 관련 주제를 뭘로할까 고민하다가 사이드 프로젝트 아이디어가 떠올라서 그걸 같이 만들어볼까 합니다. 하지만 이미 잘 알고 있던 기술을 사용해서 만들면 재미없겠죠. 사이드 프로젝트는 역시 신기술을 이용해서 만드는게 가장 좋습니다. 나만의 프로젝트를 만들면서 최신 기술도 마음껏 써볼 수 있으니까요.
평생 무료로 반응형 이메일 템플릿 무한대로 만들기 - mjml.io
저는 이메일을 데스크톱과 모바일 환경에서 매일매일 확인합니다. 그런데 아직도 모바일 디스플레이에 최적화되지 않은 이메일을 받을 때가 많습니다.
평생 무료로 커스텀 이메일 사용하기
안녕하세요. 또 다시 찾아온 평생 무료 시리즈입니다. 저는 틈만나면 1인 사이드 프로젝트를 진행하기 때문에, 어떻게든 공짜로 서버를 돌리기 위해 온갖 노력을 하고 있습니다. 그래서 무료로 이용하는 방법에 관한 글을 몇 개 올렸는데 GA를 살펴보니 다른 주제보다 조회수가 높더군요. 역시 공짜가 좋네요.
클립보드 이미지를 1초만에 링크로 만드는 툴 개발하기
저는 이 블로그를 운영하면서 가장 귀찮은 일이 하나 있습니다. 바로 이미지 주소를 만드는 일인데요, 저는 @nuxt/content 모듈을 이용해 마크다운 포맷을 이용하는 정적 블로그를 운영 중이라 글 작성 중에 원격 이미지 주소를 삽입하는 기능을 사용하지 않습니다.
평생 무료인 모니터링 도구 10분만에 만들기
서버를 운영하다보면 예상치 못한 서버 다운이나 응답 속도 저하를 반드시 겪게 됩니다. 원인은 둘째 치구요. 근데 문제는 서버 장애를 원천 차단할 방법이 사실상 없기 때문에, 우리 개발자들이 24시간 눈을 뜨고 지켜볼 수 밖에 없겠습니다.
Vue.js로 크롬 확장 프로그램 만들기 강의 - 3부
이전 포스팅에서는 Vite을 이용해 크롬 확장 프로그램을 만들기 위한 기본적인 프로젝트 환경 설정까지 마쳤습니다. 본격적으로 Vue.js 코드를 작성해보도록 합시다.
정말 너무 쉬운 Docker
우리가 Docker를 사용해야하는 가장 큰 이유는, 어떤 컴퓨터에서든 똑같은 개발 환경을 보장해주기 떄문입니다. 로컬 컴퓨터에서 열심히 개발하고 AWS에 코드를 올렸는데, 에러를 마주하며 스트레스를 받았던 경험이 한 번쯤은 있을겁니다. 내 컴퓨터랑 클라우드 컴퓨터의 환경이 100% 똑같지 않기 때문이죠. 근데 이 어려움을 한 번에 해결해준다? 쓰지 말아야 할 이유가 없습니다.
웹소켓과 socket.io
예전에 회사 프로젝트를 진행할 때, 지도에 실시간으로 사용자의 위치를 보여주는 기능이 필요해서 socket.io 를 사용해서 구현했던 적이 있습니다.
Vue.js로 크롬 확장 프로그램 만들기 강의 - 2부
이전 포스팅에서 index.html과 manifest.json 파일을 이용해서 확장 프로그램을 개발자 모드로 실행시키는 것 까지 진행했습니다.
Vue.js로 크롬 확장 프로그램 만들기 강의 - 1부
제가 최근 우연히 크롬 확장 프로그램을 개발했는데, 이게 생각보다 꽤 괜찮은 시장이라는 걸 알게 되었습니다.
웹팩보다 100배 빠른 번들러, esbuild
이번 포스팅은 떠오르는 차세대 자바스크립트 번들러 esbuild에 대한 내용입니다. 작년 Github에서 떠오르는 번들링 프로젝트 중 1위를 차지했고, 오늘을 기준으로 20만개의 가까운 Github Star를 받았습니다.
Rollup.js - 번들링, 파일을 하나로 합쳐보자
번들링 이라는 말을 프론트엔드 개발자라면 많이 들어보셨을겁니다. 번들링은, 파일을 하나로 묶는 것을 말합니다. 그럼 왜 굳이 파일을 하나로 묶어야 할까요? 바로 HTTP 통신의 특성 때문입니다.
코인 시세 1초만에 보는 크롬 확장 프로그램 만들기
가상화폐 거래소 API를 활용해 브라우저에서 단축키로 빠르게 코인 시세를 확인할 수 있는 툴을 크롬 확장 프로그램으로 만들어보았다.
평생 무료로 개인 블로그 운영하기
거의 대부분의 개발자들이 개인 블로그를 운영하라고 얘기한다. 나도 그렇게 생각한다. 왜냐면 분명히 내가 작성했던 코드인데도, 일주일만 지나도 기억이 안나기 때문이다. 그리고 웬만하면 공개해서 작성하라고 하고 싶다. 이미 우리는 누군가가 옛날에 썼던 글을 보고, 문제를 해결한 경험히 굉장히 많기 때문이다. 나는 이런 개발자들의 문화가 너무 좋다. 이런 개발자들의 문화가 다른 업종에도 접목된다면 정말 좋으련만.
Firebase를 대체할 오픈소스 프로젝트, Supabase
Supabase는 구글 Firebase를 엔터프라이즈 레벨에서도 사용 가능하도록 만든 오픈소스 프로젝트이다. 현재는 베타 서비스이다.