Bun vs Node.js 2026 — 성능, 호환성, 실사용 비교
Bun과 Node.js 비교. 벤치마크 성능, 패키지 호환성, 실제 프로젝트에서의 차이점 정리.

Node.js가 자바스크립트 서버 사이드의 유일한 선택지이던 시절이 있었다. Deno가 나왔을 때도 "재밌는 프로젝트네" 정도였는데, Bun이 등장하면서 분위기가 달라졌다. 벤치마크에서 Node.js를 압도하는 숫자가 나오니까 "이거 진짜 바꿔야 하나?" 하는 사람이 생겼다.
결론부터 말하면, 2026년 현재 대부분의 프로덕션 프로젝트에서는 Node.js가 여전히 안전한 선택이다. 근데 Bun이 못 쓸 물건이라는 뜻은 아니다. 상황에 따라 답이 다르다.
Bun이 뭔데
Bun은 Jarred Sumner가 만든 자바스크립트 런타임이다. Zig 언어로 작성했고, JavaScriptCore(Safari의 JS 엔진)를 사용한다. Node.js가 C++과 V8(Chrome의 JS 엔진) 기반인 것과 대비된다.
런타임만 제공하는 게 아니라 번들러, 테스트 러너, 패키지 매니저까지 내장되어 있다. Node.js에서는 webpack/vite, jest/vitest, npm/yarn/pnpm을 따로 설치해야 하는 걸 Bun 하나로 해결하겠다는 거다.
# Node.js 생태계
npm install # 패키지 설치
npx vitest # 테스트
npx vite build # 번들링
# Bun
bun install # 패키지 설치 (훨씬 빠름)
bun test # 테스트 (내장)
bun build ./src/index.ts # 번들링 (내장)
성능 차이
Bun이 가장 내세우는 게 성능이다. 공식 벤치마크를 보면 숫자가 화려하다.
패키지 설치 속도 — bun install은 npm install보다 수 배에서 수십 배 빠르다. 로컬 캐시가 있을 때는 거의 즉시 완료된다. pnpm도 빠른 편인데 Bun이 더 빠르다.
HTTP 서버 처리량 — 단순 HTTP 응답 벤치마크에서 Bun이 Node.js보다 2~4배 높은 RPS(초당 요청 수)를 기록한다.
스크립트 시작 속도 — bun run script.ts가 node script.js보다 빠르게 시작된다. TypeScript를 트랜스파일 없이 바로 실행하는 것도 한몫한다.
근데 벤치마크 숫자를 그대로 실전에 대입하면 안 된다. 실제 웹 서버의 병목은 보통 DB 쿼리, 외부 API 호출, 비즈니스 로직이지 런타임 자체가 아니다. HTTP 응답만 찍는 벤치마크에서 2배 빨라도, 실제 앱에서 체감되는 차이는 그보다 훨씬 작을 수 있다.
의미 있는 차이가 나는 건 패키지 설치 속도와 스크립트 시작 속도다. CI/CD에서 bun install이 30초 만에 끝나면 매 빌드마다 시간을 절약할 수 있고, 로컬 개발에서 TypeScript 파일을 바로 실행하는 건 DX(개발자 경험) 측면에서 확실히 편하다.
Node.js 호환성
Bun은 Node.js API의 95% 이상을 지원한다. fs, path, http, crypto 같은 핵심 모듈은 물론이고, 복잡한 네이티브 모듈(buffer, fs, path 포함)도 대부분 호환된다. Bun 내부적으로 Node.js 호환 버전 문자열을 보고할 정도로 호환성에 공을 들이고 있다. package.json의 scripts도 bun run으로 실행된다.
호환되지 않는 부분은:
- Node.js 네이티브 모듈(C++ 애드온) —
node-gyp로 빌드하는 모듈은 Bun에서 작동하지 않을 수 있다. bcrypt, sharp 같은 패키지가 해당. 다만 sharp는 최근 Bun을 공식 지원하기 시작했고, 이런 지원 범위는 점점 넓어지고 있다. - Node.js 특유의 내부 API —
vm모듈의 일부 기능,worker_threads의 세부 동작 등에서 미묘한 차이가 있을 수 있다. - 프레임워크 호환성 — Express는 잘 돌아간다. Fastify도 대부분 된다. Next.js는 부분적으로 지원하지만, Vercel 배포 시에는 Node.js 런타임을 쓰게 된다. NestJS는 호환성 이슈가 보고된 바 있다.
npm에 있는 패키지의 대부분은 Bun에서도 작동한다. 근데 "대부분"이 100%는 아니다. 프로젝트에서 쓰는 핵심 패키지가 Bun에서 잘 돌아가는지는 직접 확인해야 한다.
Node.js의 강점
생태계 성숙도. 16년 넘게 쌓인 생태계가 있다. npm에 200만 개 이상의 패키지가 있고, 대부분이 Node.js를 기준으로 테스트됐다. 트러블슈팅할 때 Stack Overflow에 답이 있을 확률이 높다.
안정성. 프로덕션에서 수년간 검증된 런타임이다. 메모리 누수 패턴, 성능 프로파일링, 디버깅 도구가 성숙하다. Node.js의 --inspect 플래그로 Chrome DevTools 연결하는 식의 디버깅 워크플로우는 잘 확립되어 있다.
LTS 정책. 짝수 버전은 LTS(Long-Term Support)로 30개월 동안 보안 패치를 받는다. 기업 환경에서 중요한 부분이다.
클라우드 지원. AWS Lambda, Google Cloud Functions, Azure Functions 전부 Node.js를 1급 시민으로 지원한다. Bun을 서버리스 환경에서 쓰려면 커스텀 런타임을 세팅해야 하는 경우가 많다.
어떤 상황에서 Bun이 유리한가
새 프로젝트를 빠르게 시작할 때. bun init, bun install, bun run의 속도가 빨라서 프로토타이핑에 좋다. TypeScript를 설정 없이 바로 실행하는 것도 편하다.
스크립트나 CLI 도구. 짧게 실행되는 스크립트에서는 시작 속도 차이가 체감된다. ts-node나 tsx 대신 bun run script.ts를 쓰면 빠르다.
내장 도구가 충분한 프로젝트. Bun의 번들러, 테스트 러너, 패키지 매니저만으로 커버 가능하면 의존성이 줄어든다.
패키지 설치가 잦은 CI/CD. bun install의 속도 차이가 매 빌드마다 쌓이면 시간 절약이 유의미하다.
어떤 상황에서 Node.js가 나은가
프로덕션 서비스를 운영할 때. 안정성과 디버깅 도구의 성숙도가 중요하다면 Node.js가 맞다.
네이티브 모듈 의존성이 있을 때. bcrypt, canvas, 이미지 처리 라이브러리 등 C++ 바인딩이 필요한 패키지를 쓴다면 Node.js가 안전하다.
팀 프로젝트. 팀원 모두가 Bun에 익숙하지 않다면 전환 비용이 생긴다. Node.js는 대부분의 개발자가 이미 알고 있다.
서버리스 환경. AWS Lambda 등에서 기본 지원되는 건 Node.js다.
마이그레이션, 생각보다 단순하지 않다
기존 Node.js 프로젝트를 Bun으로 옮기는 건 bun install만 치면 되는 게 아니다. 겉으로는 간단해 보여도 실제로 해보면 여러 곳에서 걸린다.
lock 파일 전환. package-lock.json이나 yarn.lock에서 bun.lockb로 바뀐다. Bun의 lock 파일은 바이너리 포맷이라 git diff로 변경 사항을 확인할 수 없다. 코드 리뷰에서 의존성 변경을 추적하기 어려워지는 건 팀 프로젝트에서 꽤 불편한 부분이다. bun.lock (텍스트 기반) 포맷도 지원하긴 하지만, 아직 기본값은 바이너리다.
환경 변수 로딩. Bun은 .env 파일을 자동으로 로드한다. Node.js에서 dotenv를 써서 로딩하던 프로젝트라면 동작이 달라질 수 있다. 로딩 순서나 우선순위가 다를 수 있으니 .env.local, .env.production 같은 파일이 여러 개인 경우 주의가 필요하다.
TypeScript 설정. Bun은 TypeScript를 네이티브로 실행하니까 tsconfig.json의 module, moduleResolution 설정이 달라질 수 있다. 특히 "type": "module" 설정과 ESM/CJS 혼용 프로젝트에서 import 경로가 꼬이는 경우가 있다.
테스트 코드. Jest나 Vitest에서 bun test로 전환할 때, mock 관련 API가 미묘하게 다르다. jest.mock()이 mock.module()로 바뀌고, 타이머 mock도 API가 다르다. 테스트 수가 많은 프로젝트에서는 이 부분만 해도 작업량이 적지 않다.
점진적으로 접근하는 게 현실적이다. 먼저 bun install만 도입해서 패키지 설치 속도를 개선하고, 이후에 스크립트 실행, 테스트, 런타임 순서로 바꿔나가는 식이다.
npm 생태계 호환성 — 뭐가 되고 뭐가 안 되나
"npm 패키지 95% 호환"이라고 하는데, 나머지 5%에 내 프로젝트의 핵심 패키지가 포함될 수 있다. 카테고리별로 정리하면 이렇다.
잘 되는 것들:
- 웹 프레임워크 — Express, Koa, Hono는 문제없다. Fastify도 대부분의 플러그인이 작동한다.
- ORM — Prisma는 Bun 공식 지원을 시작했다. Drizzle ORM도 잘 된다. TypeORM은 일부 데코레이터 관련 이슈가 보고됐지만 기본적인 사용은 가능하다.
- 유틸리티 — lodash, date-fns, zod, yup 같은 순수 JS 패키지는 당연히 문제없다.
- HTTP 클라이언트 — axios, got, undici 전부 작동한다.
주의가 필요한 것들:
- bcrypt — 네이티브 바인딩을 쓰는 버전은 안 될 수 있다.
bcryptjs(순수 JS 구현)로 대체하면 해결된다. 성능이 약간 떨어지긴 하지만 대부분의 경우 문제될 수준은 아니다. - sharp — 이미지 처리 라이브러리. Bun 1.1부터 공식 지원이 추가됐다. 이전 버전에서는 안 됐으니 Bun 버전 확인이 필요하다.
- Puppeteer/Playwright — 브라우저 자동화 도구. Playwright는 Bun에서 작동하지만, Puppeteer는 일부 기능에서 문제가 보고된 적 있다.
- node-canvas — Cairo 기반 네이티브 모듈이라 Bun에서 작동하지 않는다.
@napi-rs/canvas같은 대안을 찾아야 한다.
안 되는 것들:
- node-gyp 기반 오래된 네이티브 모듈 — C++ 애드온 중 오래된 것들은 대체로 안 된다.
node-sass(이미 deprecated),sqlite3(better-sqlite3로 대체 가능) 등이 해당한다. - vm 모듈 의존 패키지 —
isolated-vm같은 격리 실행 관련 패키지는 호환되지 않는다. - 특정 APM/모니터링 도구 — Datadog APM, New Relic 에이전트 등 Node.js 내부에 깊이 후킹하는 도구는 Bun에서 제대로 작동하지 않을 수 있다.
프로젝트 전환 전에 bun install && bun run build && bun test를 돌려보는 게 가장 확실하다. 빌드까지 성공하면 대부분 괜찮고, 런타임에서 터지는 건 그때 가서 하나씩 잡으면 된다.
Bun 테스트 러너 vs Jest/Vitest
Bun에 내장된 테스트 러너는 꽤 쓸만하다. Jest 호환 API를 제공하기 때문에 기존 테스트 코드를 크게 수정하지 않아도 돌아가는 경우가 많다.
// bun test 예시 — Jest와 거의 동일한 문법
import { describe, expect, test } from "bun:test";
describe("math", () => {
test("addition", () => {
expect(1 + 2).toBe(3);
});
});
속도 비교. bun test는 Jest보다 확실히 빠르다. Jest가 느린 이유 중 하나가 각 테스트 파일을 별도 워커에서 돌리면서 생기는 오버헤드인데, Bun은 이 부분을 최적화했다. Vitest도 빠르지만, Bun이 TypeScript 트랜스파일 단계를 건너뛰는 만큼 시작 속도에서 앞선다. 소규모 프로젝트에서는 차이가 1~2초 정도지만, 테스트 파일이 수백 개인 프로젝트에서는 차이가 커진다.
지원하는 기능. describe, test, expect, beforeAll, afterAll, beforeEach, afterEach는 전부 된다. 스냅샷 테스트도 지원한다. mock.module()로 모듈 모킹도 가능하다. 코드 커버리지도 내장되어 있어서 bun test --coverage로 바로 확인할 수 있다.
부족한 부분. Jest 에코시스템에 비하면 아직 커버하지 못하는 영역이 있다. 커스텀 매처 라이브러리(jest-extended 같은)를 바로 가져다 쓸 수 없고, IDE 통합도 Jest/Vitest만큼 매끄럽지 않다. VS Code의 Jest Runner 확장처럼 테스트를 클릭해서 개별 실행하는 식의 워크플로우는 아직 지원이 부족하다. 그리고 jest.config.js에서 transform 옵션으로 SCSS나 이미지 파일을 mock하던 프로젝트는 Bun 테스트 러너에서 별도 설정이 필요하다.
간단한 유닛/통합 테스트 위주라면 bun test로 충분하다. 복잡한 테스트 설정이 필요한 대규모 프로젝트에서는 Vitest가 아직 더 안정적인 선택이다.
Bun을 쓰면 안 되는 경우
만능 도구란 없다. Bun이 맞지 않는 상황을 구체적으로 짚어보면:
Windows 환경. Bun의 Windows 지원은 상당히 개선됐지만, Linux/macOS에 비하면 아직 엣지 케이스가 있다. 팀원 중 Windows 사용자가 있다면 사전에 테스트가 필수다. WSL2에서 돌리는 건 괜찮지만, 네이티브 Windows에서는 파일 시스템 관련 동작이 다른 경우가 간혹 있다.
APM/모니터링이 중요한 서비스. Datadog, New Relic, Sentry 같은 APM 도구의 Node.js 에이전트는 V8의 내부 API나 async_hooks에 의존한다. Bun은 JavaScriptCore 기반이라 이런 도구가 제대로 동작하지 않을 수 있다. 프로덕션에서 상세한 트레이싱이 필요하다면 Node.js가 현실적이다.
엄격한 LTS 정책이 필요한 기업. Bun은 아직 LTS 정책이 확립되지 않았다. 버전 업데이트가 빠르고 breaking change도 종종 있다. 반년에 한 번 런타임 버전을 올리는 게 아니라 매달 업데이트를 따라가야 할 수도 있다. 금융, 의료, 공공 분야처럼 변경 관리가 엄격한 환경에서는 부담이 된다.
대규모 모노레포. Bun의 워크스페이스 지원은 npm/yarn/pnpm에 비하면 아직 성숙하지 않다. 패키지 간 심볼릭 링크 처리나 hoisting 동작에서 차이가 있고, Turborepo 같은 모노레포 도구와의 호환성도 완벽하지 않다.
Bun이 안 맞는 상황에서 억지로 쓰면 오히려 시간을 낭비한다. 도구는 상황에 맞게 고르는 거다.
같이 쓰는 방법도 있다
꼭 하나만 골라야 하는 건 아니다. Bun의 패키지 매니저만 가져다 쓰는 식의 부분 도입도 가능하다.
// package.json
{
"packageManager": "bun@1.2.0"
}
bun install로 패키지를 설치하고 런타임은 Node.js를 쓰는 조합. 패키지 설치 속도의 이점은 가져가면서 런타임 호환성 걱정은 안 해도 된다.
로컬 개발에서는 bun run으로 TypeScript를 빠르게 실행하고, 배포할 때는 Node.js로 빌드하는 식도 된다.
Bun이 빠른 속도로 버전 업데이트를 이어가고 있고, 이미 프로덕션에서 채택하는 팀도 늘고 있다. Node.js와 직접 경쟁하는 수준까지 올라왔고, 주요 프레임워크들이 공식 지원을 시작하면서 하이브리드 전략(Bun 패키지 매니저 + Node.js 런타임)부터 시작해서 점진적으로 전환하는 방식이 현실적이다. 새 프로젝트를 시작할 때 한번 써보면서 자기 환경에서의 호환성과 체감 성능을 확인해보는 걸 추천한다.