Bun: 귀엽지만 강력한 새로운 자바스크립트 런타임
2023년에 번(Bun) v1.0이 출시되면서 차세대 자바스크립트 런타임(Runtime)으로 많은 기대를 한몸에 받았는데요. 같은 해 JavaScript Rising Stars에서도 Bun이 당당히 2위를 차지하였습니다.
이번 포스팅에서는 귀여운 이름과 로고 뒤에 무시무시한 기능과 성능으로 무장하고 하고 있는 Bun이라는 새로운 자바스크립트 런타임에 대해서 살펴보겠습니다.

자바스크립트 런타임
Bun에 대해서 소개드리기 전에 먼저 자바스크립트 런타임(Runtime)이 무엇인지 가볍게 짚고 넘어가면 좋을 것 같아요.
자바스크립트 런타임이란 쉽게 말해 자바스크립트로 작성된 프로그램을 실행해주는 소프트웨어를 의미합니다.
가장 흔한 예로, 우리가 매일 사용하는 크롬이나 사파리, 파이어폭스, 엣지와 같은 웹 브라우저를 들 수 있죠.
웹 브라우저는 HTML 문서에서 <script/> 태그를 통해 삽입된 자바스크립트 코드를 실행하여 웹페이지가 사용자와 상호작용할 수 있도록 도와줍니다.
자바스크립트 초창기에는 자바스크립트로 작성된 코드는 브라우저를 통해서 클라이언트 측에만 실행할 수 있었습니다. 그러나 2009년에 Node.js가 등장하면서 서버 측에도 자바스크립트 코드를 실행할 수 있는 길이 열리게 됩니다. 다시 말해서, 프런트엔드뿐만 아니라 백엔드에서 자바크립트를 사용할 수 있게 된 것 인데요. Node.js는 C++로 개발되었으며, 그 이후에 Node.js 만드신 분이 Rust라는 새로운 언어로 Deno라는 또 다른 자바스크립트 런타임도 개발하기도 했죠.
Bun은 Node.js와 Deno처럼 기본적으로 백엔드에서 사용하기 위해서 만들어진 자바스크립트 런타임이며, Zig라는 아직 많은 분들에게 생소할 수도 있는 프로그래밍 언어로 개발이 되었습니다. 참고로 2026년에 출시된 Bun 1.4부터는 이 Zig 코드베이스가 통째로 Rust로 재작성되었습니다.
참고로 하나의 프로그래밍 언어에 여러 개의 런타임이 있다는 것은 젼혀 이상한 현상이 아니에요. 예를 들어, 파이썬(Python)에도 CPython, PyPy, Jython 등 다양한 런타임이 있고요, 루비(Ruby)에도 MRI, JRuby, Rubinius 등 다양한 런타임이 있습니다.
Bun 설치
Bun은 MacOS와 같은 리눅스 계열 운영체제를 사용하고 계신다면 curl 명령어로 간편하게 설치해서 사용해볼 수 있습니다.
$ curl -fsSL https://bun.sh/install | bash
다음과 같이 터미널에서 Bun의 버전이 확인된다면 설치가 완료된 것입니다.
$ bun -v
1.4.0
이 글을 처음 쓸 당시에는 윈도우즈에서 Bun을 온전하게 쓰기 어려웠는데요. v1.1부터 네이티브 윈도우즈 빌드가 제공되어 이제는 WSL 없이 파워셸(PowerShell)에서 바로 설치할 수 있습니다.
$ powershell -c "irm bun.sh/install.ps1|iex"
Bun 프로젝트 생성
터미널에서 bun init 명령어를 실행하면 쉽게 Bun 프로젝트를 생성할 수 있는데요.
패키지 이름과 진입 지점(entry point)를 대화형으로 물어보는데 귀찮으면 -y 옵션을 사용하면 됩니다.
그러면 현재 폴더명이 패키지 이름으로 사용되고 index.ts 파일이 진입 지점이 됩니다.
$ bun init -y
+ .gitignore
+ CLAUDE.md
+ index.ts
+ tsconfig.json (for editor autocomplete)
+ README.md
To get started, run:
bun run index.ts
bun install v1.4.0 (34cbb9a40)
Resolving dependencies
Resolved, downloaded and extracted [8]
Saved lockfile
+ @types/bun@1.4.0
+ typescript@7.0.2
6 packages installed [922.00ms]
프로젝트 뼈대만 만들고 끝나는 게 아니라 타입 정의 패키지까지 알아서 설치해준다는 점이 눈에 띄는데요.
AI 코딩 도구에게 프로젝트 규칙을 알려주는 CLAUDE.md 파일도 함께 생성됩니다.
템플릿을 이용해서 Bun 프로젝트를 시작하고 싶다면 bun create 명령어를 사용할 수도 있습니다.
템플릿 이름을 넘기면 npm에서 그에 대응하는 create-* 패키지를 찾아서 실행해줍니다.
$ bun create vite
깃허브 저장소를 계정/저장소 형태로 넘기면 해당 저장소를 템플릿으로 내려받아 프로젝트를 만들어주고요.
$ bun create <user>/<repo> my-app
타입스크립트 실행
Bun의 가장 큰 매력은 타입스크립트로 작성된 프로그램을 자바스크립트로 변환하지 않고 바로 실행할 수 있다는 것인데요. 즉, Bun은 타입스크립트 런타임이라고도 부를 수도 있겟습니다.
예를 들어, 아주 간단한 타입스크립트 프로그램을 작성해보겠습니다.
let message: string = "안녕하세요!";
console.log(message);
다음과 같이 이 프로그램 파일은 Bun을 통해서 터미널에서 바로 실행할 수 있습니다.
$ bun index.ts
안녕하세요!
만약에, 동일한 작업을 Node.js로 하려면 어떻게 했어야 했을까요?
우선 타입스크립트로 작성된 프로그램을 tsc 커맨드로 컴파일(compile)했어야 겠죠?
$ npx tsc index.ts
그래야지 결과물로 자바스크립트 파일을 얻을 수 있을테니까요.
var message = "안녕하세요!";
console.log(message);
그 다음에 이 자바스크립트 프로그램을 Node.js로 실행했어야 했을 것입니다.
$ node index.js
안녕하세요!
타입스크립트 코드를 자바스크립트로 코드로 컴파일하는 기본적인 방법에 대해서는 별도 포스팅을 참고 바랍니다.
물론, 이 두 단계의 작업을 한 번으로 줄여주는 ts-node나 tsx와 같은 개발 도구를 사용할 수도 있는데요.
이 방법은 추가 패키지를 설치해줘야 한다는 나름의 귀찮음이 있습니다.
$ npm install -D ts-node
$ npx ts-node index.ts
안녕하세요!
Bun을 사용하면 이와 같은 번거로운 컴파일이나 개발 도구 설치가 필요 없어지기 때문에 개발 생산성이나 개발자 경험을 크게 향상시킬 수 있습니다.
사실 이 부분은 Bun이 자극제가 되어 생태계 전체가 따라온 대표적인 사례이기도 한데요. 이 글을 처음 쓸 때만 해도 타입스크립트를 바로 실행하는 것은 Bun의 확실한 차별점이었지만, Node.js도 v23.6부터(LTS 계열은 v22.18부터) 별도 플래그 없이 타입스크립트 파일을 실행할 수 있게 되었습니다.
$ node index.ts
안녕하세요!
다만 두 런타임이 타입스크립트를 처리하는 방식은 다릅니다. Node.js는 타입 표기만 지워내는 타입 제거(type stripping) 방식이라서, 실행할 때 코드 생성이 필요한 문법을 만나면 오류가 납니다. 예를 들어 열거형(enum)은 자바스크립트에 대응하는 문법이 없어서 실제 객체를 만들어줘야 하는데요.
enum Color {
Red,
Green,
}
console.log(Color.Red);
$ node enum-test.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode
반면에 Bun은 트랜스파일러를 통째로 내장하고 있기 때문에 이런 제약이 없습니다.
$ bun enum-test.ts
0
모듈 시스템 호환성
현재 자바스크립트 생태계에서는 수년에 걸쳐 상당히 고통스러운 모듈 시스템의 전환 작업이 일어나고 있습니다.
예전에는 모듈을 내보내거나 불러오기 위해서 Node.js를 중심으로 CJS(CommonJS)를 많이 사용했는데요. 최근에는 ES6(ES2015)에서 ESM(ES Module)이라는 새로운 모듈 시스템이 표준으로 채택이 되면서, 신규 프로젝트에서는 ESM을 사용하는 것을 권장하는 분위기입니다. 하지만 npm 패키지 저장소에는 10년을 훌쩍 넘는 기간동안 CommonJS을 사용한 패키지들이 축적되어 왔고, 그 중 많은 오픈 소스 패키지는 더 이상 업데이트가 되지 않아서 ESM으로 언제 전환될지 알 수 없는 상황이죠.
그러므로, 우리는 아직까지도 모듈을 내보내거나 불러올 때 CommonJS를 완전히 배제할 수 없는 상황인데요. ESM 프로젝트에서 CJS 모듈을 불러오거나, 반대로 CJS 프로젝트에서 ESM 모듈을 불러올 때 갖가지 해결하기 까다로운 문제들이 발생할 수 있죠.
자바스크립트의 모듈 시스템을 양분하고 있고 있는 CJS와 ESM에 대해서 더 궁금하시는 분들께는 다음 포스팅을 추천드리겠습니다.
하지만 Bun은 이렇게 골치 아플 수 있는 CJS와 ESM 간의 모듈 시스템 호환 문제를 런타임 수준에서 깔끔하게 해결해줍니다.
예를 들어, 인자로 넘어온 메시지를 콘솔에 출력해주는 hello()라는 함수를 작성해보겠습니다.
hello.cjs 파일에는 CJS 방식으로 함수를 내보내겠습니다.
exports.hello = function (message) {
console.log("CJS:", message);
};
hello.mjs 파일에는 ESM 방식으로 함수를 내보내겠습니다.
export function hello(message) {
console.log("ESM:", message);
}
이제 index.ts 파일에서 작성한 함수를 불러와볼까요?
우선 두 파일을 모두 ESM의 import 키워드를 사용하서 불러올 수 있습니다.
import { hello } from "./hello.cjs";
hello("안녕하세요");
$ bun index.ts
CJS: 안녕하세요
import { hello } from "./hello.mjs";
hello("안녕하세요");
$ bun index.ts
ESM: 안녕하세요
뿐만 아니라 두 파일을 모두 CJS의 require 키워드를 사용하서 불러올 수 있습니다.
const { hello } = require("./hello.cjs");
hello("안녕하세요");
$ bun index.ts
CJS: 안녕하세요
const { hello } = require("./hello.mjs");
hello("안녕하세요");
$ bun index.ts
ESM: 안녕하세요
더 놀라운 부분은 파일을 불러올 때 확장자를 명시하지 않으면, 똑똑하게 알아서 모듈 시스템에 맞는 파일을 선택해서 불러 온다는 것입니다.
예를 들어, import 키워드를 썼을 때는, hello.mjs 파일에서 불러옵니다.
import { hello } from "./hello";
hello("안녕하세요");
$ bun index.ts
ESM: 안녕하세요
반면에 require 키워드를 썼을 때는, hello.cjs 파일에서 불러오는 것을 볼 수 있습니다.
const { hello } = require("./hello");
hello("안녕하세요");
$ bun index.ts
CJS: 안녕하세요
패키지 매니저 내장
Node.js에서는 npm이 표준 패키지 매니저임에도 불구하고, Yarn이나 Pnpm과 같은 서드 파티 패키지 매니저도 많이 사용되고 있습니다. 이렇게 다양한 패키지 매니저가 사용되는 이유는 아무래도 표준 패키지 매니저인 npm이 여러가지 부분에서 자바스크립트 커뮤니티의 기대에 부흥하지 못해서가 아닐까 싶은데요. 물론 최근에 출시되고 있는 버전의 npm에서는 이렇게 부족한 부분이 많이 보완되어서 npm으로 회귀하는 프로젝트도 많아지고 있습니다.
반면에, Bun은 아예 자체적으로 패키지 매니저를 내장하고 있으며, npm이나 Yarn과 같은 기존에 많이 사용되던 패키지 매니저랑 호환까지 됩니다.
예를 들어, package.json 파일에 명시되어 있는 모든 패키지를 설치하고 싶다면 bun install 명령어를 실행하면 됩니다.
$ bun install
새로운 패키지를 추가하고 싶다면 bun add 명령어를 사용하면 되고요.
$ bun add react
기존 패키지를 제거하고 싶다면 bun remove 명령어를 사용하면 됩니다.
$ bun remove react
명령줄 인터페이스(CLI)가 기존에 우리가 쓰던 패키지 매니저와 거의 동일하기 때문에 큰 이질감없이 사용할 수 있다는 장점이 있습니다. 게다가 더 이상 어떤 패키지 매니저를 선택할지 고민할 필요도 없어지고요.
초기에는 설치 속도만 앞서고 부가 명령어가 부족하다는 아쉬움이 있었는데요.
Bun 1.4에서는 중복 버전을 정리하는 bun dedupe, 쓰지 않는 패키지를 지우는 bun prune,
취약한 패키지를 안전한 버전으로 올려주는 bun audit fix까지 채워지면서 npm이나 pnpm과 기능 차이가 거의 없어졌습니다.
테스트 러너 내장
자바스크립트에서는 보통 테스트 코드를 실행할 때 Jest와 같은 별도의 라이브러리를 사용하는데요. 다른 프로그래밍 언어를 보면 런타임에서 테스트 러너(Runner)를 지원하는 경우가 많습니다. 왜냐하면 테스트를 작성하고 실행하는 작업은 대부분의 소프트웨어 프로젝트에서 필수적이기 때문입니다.
이러한 이유로 자바스크립트 런타임도 테스트 실행 기능과 관련 유틸리티를 내장하는 경우가 점점 늘어나고 있는데요. Node.js의 테스트 러너 API는 v20에서 안정화가 되었고, Bun도 아예 처음부터 자체 테스트 러너를 내장하여 출시되었습니다.
bun:test 패키지를 통해서 테스트에 필요한 대부분의 유틸리티를 불러와서 사용할 수 있으며,
import { expect, test, spyOn } from "bun:test";
import { hello } from "./hello";
test("hello", () => {
const spy = spyOn(console, "log");
hello("안녕?");
expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.calls[0]).toEqual(["ESM:", "안녕?"]);
});
터미널에서 bun test 명령어를 통해서 작성한 테스트를 실행할 수 있습니다.
$ bun test
bun test v1.4.0 (34cbb9a40)
hello.test.ts:
ESM: 안녕?
1 pass
0 fail
2 expect() calls
Ran 1 test across 1 file. [9.00ms]
테스트 파일이 늘어나면 실행 시간이 부담스러워지기 마련인데요.
Bun 1.4부터는 --parallel 옵션으로 여러 워커 프로세스에 테스트 파일을 나눠서 돌릴 수 있고,
--isolate를 함께 주면 파일마다 새 전역 객체를 할당해서 테스트끼리 전역 상태를 오염시키는 문제도 막을 수 있습니다.
$ bun test --parallel --isolate
트랜스파일러/번들러 내장
그런데 Bun을 쓰면 항상 컴파일 과정을 생략할 수 있을까요? 이 부분 오해하기 쉬운데요. Bun을 쓰더라도 프론트엔드 프로젝트에서는 타입스크립트로 작성된 코드를 자바스크립트를 변환하는 작업은 필수입니다. 왜냐하면 브라우저는 타입스크립트로 작성된 코드를 그대로 실행할 수 없기 때문입니다.
하지만 Bun은 마치 타입스크립트처럼 JSX로 쓰여진 코드도 트랜스파일(transpile)없이 바로 처리할 수 있습니다.
function Button(props: { message: string }) {
return <button>{props.message}</button>;
}
console.log(<Button message="Hello world!" />);
$ bun Button.tsx
<Button message="Hello world!" />
다만 JSX 문법 자체는 Bun이 바로 처리해주더라도, 변환된 코드가 참조하는 react/jsx-runtime은 여전히 필요하기 때문에 React는 설치되어 있어야 합니다.
심지어 브라우저가 내려받아 실행해야 하는 자바스크립트 코드를 최적화하기 위해 번들러(bundler)까지 bun build라는 명령어를 통해 자체적으로 지원합니다.
따라서 Bun 하나만 있으면, TypeScript나 Babel, Webpack과 같은 개발 도구가 없이도 웹사이트 배포를 위한 애플리케이션 빌드(build)를 처리할 수 있습니다.
Web 표준 API 지원
Bun은 fetch()와 같은 Node.js가 다소 지원을 소홀히 하던 Web 표준 API도 적극적으로 지원하고 있습니다.
엣지 컴퓨팅(Edge Computing)이나 SSR(서버 사이드 렌더링)과 같은 최신 웹 개발 트랜드를 충실히 반영하려는 노력이 엿보입니다.
Bun의 뛰어난 성능
Bun의 웹사이트를 방문해보시면 기존 자바스크립트 런타임 대비 작게는 몇 배 크게는 수십 배 성능이 빠르다는 문구나 도표를 이곳 저곳에서 쉽게 접할 수 있는데요. 그만큼 Bun은 기능 뿐만 아니라 성능 측면에서도 매우 우수한 자바스크립트 런타임이라는 것을 알 수 있습니다.

Bun이 Node.js를 대체할까?
Bun은 스스로를 Node.js를 완벽하게 대체할 수 있는 자바스크립트 런타임으로 마케팅하고 있는데요. 이 글을 처음 썼던 2023년에는 “a drop-in replacement for Node.js”라는 주장이 다소 앞서간 구호처럼 들렸습니다. Node.js를 쓰던 프로젝트에 Bun을 그대로 물렸다가 라이브러리 하나 때문에 터지는 일이 드물지 않았으니까요.
그로부터 몇 년이 지난 지금은 사정이 꽤 달라졌습니다.
Bun 1.4는 Node.js 테스트 스위트에서 1,517개를 추가로 통과시켰고,
node:http와 node:fs, node:stream 같은 핵심 모듈은 Node.js 자체 테스트의 97%를 통과합니다.
node:sqlite와 node:events는 아예 100%를 채웠고요.
Playwright가 Bun 위에서 돌아가고, Next.js 16을 빌드할 수 있으며, Vitest도 실행됩니다.
이렇게 무거운 도구들이 통과했다는 건 호환성 주장이 구호를 넘어섰다는 뜻이죠.
그렇다고 해서 Node.js의 자리를 곧바로 넘겨받을 수 있을까요? 글쎄요… 2009년에 시작해 15년을 훌쩍 넘긴 Node.js의 아성을 Bun이 얼마나 빠르게 흔들 수 있을지는 여전히 지켜볼 일입니다. 2018년에 Deno가 처음 나왔을 때도 비슷한 기대와 논쟁이 있었지만 여전히 Node.js의 시장 점유율에 크게 미치지 못합니다.
하지만 Bun은 Deno가 했던 실수들을 반복하지 않으며 처음부터 npm을 지원하고 Node.js와의 호환성을 최우선하고 있어서 개발자들 사이에서 아주 긍정적인 반응을 얻고 있습니다. 뿐만 아니라, 커뮤니티 활동에 다소 인색한 Node.js에 비해, Bun의 커뮤니티는 매우 활발하게 사용자들 소통하고 있는 것으로 보입니다.
기업 입장에서 특별히 현재 성능 이슈가 있지 않다면 단순히 Bun이 더 빠르다고 해서 오랫동안 안정성이 검증된 Node.js를 떠나기는 쉽지 않을 것입니다. 아무래도 백엔드 서버의 런타임을 교체하는데는 적지 않은 위험 부담이 따르기 때문입니다. 더구나 1.4는 코드베이스를 Zig에서 Rust로 통째로 옮긴 직후의 릴리스라서, 프로덕션에 올리기 전에 한 템포 지켜보려는 팀도 많을 것 같습니다. 하지만 팀 단위로 진행되는 신규 프로젝트에서는 당장 Bun을 도입하여 개발 프로세스를 단순화하고 개발자 경험을 개선할 수도 있을 것 같습니다. 개인 프로젝트에서는 Bun을 쓰지 않을 이유를 딱히 찾기 어려울 정도로 사용성이 좋은 것 같습니다.
마치며
지금까지 차세대 자바스크립트 런타임으로 주목받고 있는 Bun에 대해서 알아보았습니다.
컴파일, 테스트, 트랜스파일링, 번들링을 Bun 하나로 해결할 수 있으니 가히 올인원(All-in-one) 런타임이라고 불러도 손색이 없을 것 같습니다. 이렇게 다양한 기능을 한 번에 제공하면서 성능도 기존의 다른 런타임을 압도할 수 있다니 정말 놀랍지 않나요?
마지막으로 Bun의 등장을 너무 기존 런타임과의 경쟁 구도에서 바라 보실 필요는 없다라고 말씀드리고 싶은데요. 저는 개인적으로 자바스크립트 생태계에 새로운 런타임이 생겼다는 것은 매우 반길만한 일이라고 생각합니다. Node.js와 Deno, Bun 그리고 앞으로 등장할 다른 런타임들이 서로의 좋은 점을 취하면서 선의의 경쟁을 펼칠테니까요. 경쟁에서 어떤 런타임이 살아남고 도태되든, 자바스크립트 개발자들은 더 나은 개발 환경을 누릴 수 있겠죠?
여러분들도 배워야 할 기술이 하나 더 늘었다고 너무 스트레스 받으시지 말고, 당장 쓰실 일은 없더라도 재미삼아 Bun을 한 번 직접 써보시기를 추천드리고 싶습니다. 😄
참고로 State of JS 2025 설문 결과에서 Bun은 처음 등장하자마자 바로 S등급을 차지하는 기염을 토했습니다.
이 글은 v1.0이 갓 나왔을 때 쓴 글이라 Bun의 기본기 위주로 다루었는데요. 그 사이 Bun은 v1.1에서 네이티브 윈도우즈를 지원하고, 1.4에서는 코드베이스를 Rust로 옮기면서 이미지 처리와 크론, 마크다운 파서까지 표준 라이브러리로 끌어안았습니다. 최신 변경점이 궁금하시다면 이어서 읽어보시기를 권해드립니다.
This work is licensed under CC BY 4.0