웹팩보다 100배 빠른 번들러, esbuild
이번 포스팅은 떠오르는 차세대 자바스크립트 번들러 esbuild에 대한 내용입니다. 작년 Github에서 떠오르는 번들링 프로젝트 중 1위를 차지했고, 오늘을 기준으로 20만개의 가까운 Github Star를 받았습니다.
웹팩보다 100배 빠르다는 건 어그로가 아닙니다. 아래 그림을 봐주시죠.
위 벤치마크는 메이저 자바스크립트 번들러들의 빌드 타임을 비교한 표입니다.
아니 어떻게 이렇게 빠를 수가 있냐구요? 이유는 이러합니다.
Go언어로 작성됨- 코드 파싱, 출력과 소스맵 생성을 모두 병렬로 처리함
- 불필요한 데이터 변환과 할당 없음
하지만 아직 1.0 버전 출시 전이기 때문에, 많은 기능을 제공하고 있지는 않습니다. 현재 지원되는 기능은 이렇습니다.
- CommonJS, ES6
- JSX
- Typescript
- Tree shaking
- Source Map
- Minification
- 등등 더 많음
훌륭합니다. 사실 이 정도만 지원되도 사용하기에 부족함이 없습니다. 오히려 빌드 타임이 너무 빨라서 프로젝트 규모가 커질수록 이득이죠.
esbuild는 es5 이하의 문법을 아직 100% 지원하지 않습니다. 즉 완벽한 인터넷 익스플로러 대응이 어렵습니다. IE 대응을 하려면 다른 대안을 찾는 것이 좋겠습니다.
꼭 알아야 할 내용은, esbuild는 자바스크립트를 위한 번들러입니다. 타입스크립트의 타입 체킹이나 프론트엔드 언어 (Vue, Angular) 지원, 핫 모듈 리로딩을 포함한 개발 서버 오픈 등 번들링과 관계 없는 기능들은 일체 없습니다. 그래서 저는 이 툴이 마음에 듭니다.
esbuild 사용해보기
먼저 프로젝트를 초기화 해줍시다.
$ mkdir esbuild
$ cd esbuild
$ yarn init -y
$ yarn add -D esbuild
esbuild는 번들러이기 때문에 최초 진입할 파일 1개가 필요합니다. 그것도 같이 만들어주도록 하겠습니다.
$ mkdir src
$ touch src/main.js
저는 이번 포스팅에서 동물들을 찍어내고, 동물들의 울음소리를 출력하는 그러한 클래스를 만들어보겠습니다.
class Animal {
constructor(sound) {
this.sound = sound;
}
Bark() {
console.log(this.sound + "!");
}
}
먼저 동물이라는 기본 베이스가 될 클래스를 만들고, 짖는 소리를 내는 Bark라는 함수를 만들었습니다.
다음은 이 동물을 상속받는 개를 한 번 만들어보도록 하겠습니다.
class Animal {
constructor(sound) {
this.sound = sound;
}
Bark() {
console.log(this.sound + "!");
}
}
class Dog extends Animal {
constructor() {
super("멍멍");
}
}
new Dog().Bark();
// 멍멍!
간단하게 개가 짖는 것 까지 만들었습니다. 그럼 바로 번들링을 해보도록 하겠습니다.
{
"scripts": {
"build": "esbuild src/main.js --bundle --outdir=dist"
}
}
기존 package.json에 scripts 키를 추가하고, build라는 명령어를 실행시키는 스크립트를 작성했습니다. --bundle 옵션은 번들링을 하겠다는 뜻이고, --outdir=dist는 최종 결과물 파일을 dist 폴더 아래에 넣겠다는 뜻입니다.
그럼 빌드를 해봅시다.
$ yarn build # npm run build
# Log
$ esbuild src/main.js --bundle --outdir=dist
✨ Done in 0.05s.
작성한 코드가 거의 없긴 하지만, 0.05초는 정말 빠르네요.
결과물 파일은 이렇습니다.
(() => {
// src/main.js
var Animal = class {
constructor(sound) {
this.sound = sound;
}
Bark() {
console.log(this.sound + "!");
}
};
var Dog = class extends Animal {
constructor() {
super("\uBA4D\uBA4D");
}
};
new Dog().Bark();
})();
애초에 ES6로 작성해서 그런지, 딱히 바뀐 건 크게 없어보입니다. 한글로 된 부분은 유니코드로 변환되었고, 상단에 코드의 출처를 주석으로 달아주었네요.
번들링을 좀 더 자세히 알아보자
용도에 맞게 번들링을 시작하기 전에 알아두면 좋은 개념이 있습니다. 바로 format 입니다. 이 포맷에 관한 내용은 esbuild 뿐만 아니라, 다른 번들러들에서도 사용되는 개념이니 알아두면 좋습니다.
포맷은 용도에 따라 3가지로 나눌 수 있습니다.
iifecjsesm
iife 는 immediately-invoked function expression의 약자이고, 브라우저에서 동작하는 포맷입니다.
cjs는 CommonJS라는 뜻이고, Node에서 default로 동작하는 포맷입니다.
마지막으로 esm은 ECMA Script라는 뜻으로, 브라우저와 노드 양쪽 모두에서 사용 가능한 포맷입니다.
내 코드가 브라우저랑 노드 양쪽 다 지원하면 좋으니까 포맷을 무조건 esm으로 해야지~ 라고 생각할 수 있지만, 당연히 결과물의 코드 양이 많아집니다. 용도에 맞게 포맷을 지정해서, 불필요하게 최종 결과물의 크기를 커지지 않도록 합시다.
esbuild에선 platform 이라는 옵션을 줘서 방금 소개한 format을 자동으로 지정해줍니다.
기본적으로 브라우저에서 사용 가능하도록 번들링하는 browser가 기본이고, 이 경우 iife포맷으로 트랜스파일링합니다.
만약 node에서만 사용 가능하도록 번들링하고 싶다면 platform 옵션을 node로 주면 됩니다. 이 경우 포맷은 cjs입니다.
아니면 브라우저와 노드 양쪽 모두에서 사용하고 싶을 땐 platform 옵션을 neutral로 설정합시다. 이 경우 포맷은 esm입니다.
저는 방금 만든 이 코드를 node에서만 사용할 예정이라, 아까 설정한 빌드 명령어를 바꾸도록 하겠습니다.
{
"scripts": {
"build": "esbuild src/main.js --bundle --outdir=dist --platform=node"
}
}
빌드 스크립트 작성하기
벌써부터 빌드 명령어가 한 줄로 길어져서 보기가 안좋습니다. 앞으로 어떤 옵션이 더 추가될지 모르는데 이런 방식은 지양하는게 좋습니다.
보통 다른 툴들은 .js나 .json 형태의 설정 파일을 만들면, 해당 파일을 자동으로 읽어서 빌드를 실행합니다. 하지만 esbuild는 자바스크립트 모듈을 직접 실행시키는 방법을 사용합니다.
바로 스크립트를 작성해보도록 하겠습니다.
$ mkdir scripts
$ touch scripts/build.js
require("esbuild")
.build({
entryPoints: ["src/main.js"],
outdir: "dist",
bundle: true,
platform: "node",
})
.catch(() => process.exit(1));
프로젝트에 scripts 폴더를 만들고, 그 아래 build.js 파일을 만들었습니다. 아까 명령어를 이해했으면, 이 설정 값도 직관적으로 바로 이해가 됩니다.
다음은 package.json에서 esbuild CLI가 아닌, 방금 작성한 자바스크립트를 실행시키게끔 코드를 변경해줍니다.
{
"scripts": {
"build": "node scripts/build.js"
}
}
다시 yarn build 명령어로 빌드를 해보면, 같은 결과가 나옵니다.
코드 모듈화하기
우리가 만든 동물 클래스를 외부로 내보내진 않았기 때문에, 모듈은 아닙니다.
모듈로 만들어야 하는 경우는, 다른 프로젝트에서 이 동물 클래스를 사용하게 만들고 싶을 때입니다. npmjs.com에 올라온 패키지들이 전부 모듈인 것이죠.
모듈로 만드는 방법은 굉장히 간단합니다. 선언 시, export라는 접두어를 붙이면 됩니다.
export class Dog extends Animal {
constructor() {
super("멍멍");
}
}
Dog 클래스에 export 접두어를 붙여주었습니다. 이 상태에서 빌드를 하면 다른 노드 프로젝트에서 이런 식으로 불러서 쓸 수 있게 됩니다.
import { Dog } from "./dist/main";
new Dog().Bark();
루트 디렉토리에 test.js를 만들었고, 잘 작동하는지 테스트하기 위해 실행시켜줍시다.
$ node test.js
import { Dog } from './dist/main'
^^^^^^
SyntaxError: Cannot use import statement outside a module
오류가 발생했습니다. 이 문제는 Node가 자바스크립트를 읽을 때 CommonJS 방식으로 해석하기 때문에 발생하는 문제입니다. 우리가 번들링한 방식은 ECMA Script 포맷이었죠.
타입스크립트
그렇다면 어떻게 테스트 하느냐?
바로 자바스크립트 트랜스파일러인 Babel을 사용하면 됩니다. 하지만 타입스크립트를 도입한다면 바벨을 쓰지 않더라도 어느정도는 해결이 가능합니다.
그래서 여기에서 typescript를 이용하면 바벨 없이 런타임에 Node를 실행시키면서, 다른 유저들을 위한 .d.ts 파일까지 지원 가능해집니다. 심지어 개발 단계에서 타입 체킹까지 가능하니 일석삼조입니다.
고맙게도 esbuild는 확장자가 .ts인 파일에 대해 자동으로 처리해줍니다.
그럼 아까 만든 파일의 확장자를 .ts로 바꿔줍시다.
export interface IAnimal {
sound: string;
}
class Animal implements IAnimal {
sound: string;
constructor(sound: string) {
this.sound = sound;
}
Bark() {
console.log(this.sound + "!");
}
}
export class Dog extends Animal {
constructor() {
super("멍멍");
}
}
이렇게만 해도 번들링은 잘 되지만, esbuild는 타입스크립트의 타입 체킹을 빌드할 때 해주지는 않습니다. 단순히 코드를 읽어서 바꿔주기만 하는 것이죠. 또 .d.ts 파일을 만들어주지도 않습니다.
공식 문서에선 esbuild가 번들링에만 치중하기 때문에, 앞으로도 지원할 가능성은 매우 낮다고 얘기합니다.
제대로 타입스크립트를 활용하려면 몇 가지 패키지를 설치하고, 설정 파일인 tsconfig.json도 필요합니다.
$ yarn add -D typescript ts-node @types/node
$ node_modules/.bin/tsc --init
두 번째 명령어를 실행하면 tsconfig.json 보일러 플레이트를 만들 수 있고, 저는 이렇게 사용하도록 하겠습니다.
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "dist",
"declaration": true,
"emitDeclarationOnly": true,
"strict": true,
"esModuleInterop": true
},
"include": ["./src/**/*"]
}
여기선 두 가지가 중요합니다.
declaration 옵션은 d.ts 파일을 만들겠다는 뜻이고, emitDeclarationOnly는 tsc 내장 번들링을 사용하지 않고, 단순 타입 체킹만 하겠다는 뜻입니다.
그럼 다음으로 CLI를 실행할 빌드 스크립트로 수정해줍시다.
"scripts": {
"build": "tsc && node scripts/build.js"
},
이제 명령어를 실행하면 tsc로 타입 체킹을 한뒤, 문제가 없다면 d.ts 파일을 만들고, 그 이후 esbuild를 이용해 빠르게 번들링하는 과정이 진행됩니다.
마무리
여기까지 간단하게 esbuild와 typescript를 이용해 아주 간단한 모듈을 만드는 것 까지 알아보았습니다.
참고
같은 카테고리의 다른 글
[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부
제가 최근 우연히 크롬 확장 프로그램을 개발했는데, 이게 생각보다 꽤 괜찮은 시장이라는 걸 알게 되었습니다.
Rollup.js - 플러그인으로 완성도를 높이다
지난 포스팅에서 rollup.js 를 이용해 두 개의 자바스크립트 파일을 하나로 묶고, rollup.config.js 파일을 구성해서 CLI가 아닌 스크립트로 설정 파일을 관리하는 것 까지 진행했습니다.
Rollup.js - 번들링, 파일을 하나로 합쳐보자
번들링 이라는 말을 프론트엔드 개발자라면 많이 들어보셨을겁니다. 번들링은, 파일을 하나로 묶는 것을 말합니다. 그럼 왜 굳이 파일을 하나로 묶어야 할까요? 바로 HTTP 통신의 특성 때문입니다.
코인 시세 1초만에 보는 크롬 확장 프로그램 만들기
가상화폐 거래소 API를 활용해 브라우저에서 단축키로 빠르게 코인 시세를 확인할 수 있는 툴을 크롬 확장 프로그램으로 만들어보았다.
평생 무료로 개인 블로그 운영하기
거의 대부분의 개발자들이 개인 블로그를 운영하라고 얘기한다. 나도 그렇게 생각한다. 왜냐면 분명히 내가 작성했던 코드인데도, 일주일만 지나도 기억이 안나기 때문이다. 그리고 웬만하면 공개해서 작성하라고 하고 싶다. 이미 우리는 누군가가 옛날에 썼던 글을 보고, 문제를 해결한 경험히 굉장히 많기 때문이다. 나는 이런 개발자들의 문화가 너무 좋다. 이런 개발자들의 문화가 다른 업종에도 접목된다면 정말 좋으련만.
Firebase를 대체할 오픈소스 프로젝트, Supabase
Supabase는 구글 Firebase를 엔터프라이즈 레벨에서도 사용 가능하도록 만든 오픈소스 프로젝트이다. 현재는 베타 서비스이다.