Bun으로 Node.js 대체하기: 2026년 실전 마이그레이션 가이드

프론트엔드

BunNode.jsJavaScript 런타임성능백엔드

이 글은 누구를 위한 것인가

  • Node.js 앱의 시작 시간과 처리량을 개선하고 싶은 팀
  • Bun의 실제 성능과 Node.js 호환성 수준을 검토하는 개발자
  • 번들러, 테스트 러너, 패키지 매니저를 단일 툴로 통합하려는 팀

들어가며

Bun은 JavaScriptCore 엔진 기반의 올인원 JS 런타임이다. Node.js 대비 시작 시간 4배 빠름, HTTP 처리량 3배 높음, 패키지 설치 10배 빠름을 주장한다. 2026년 현재 Node.js 호환성은 95% 이상이다.

이 글은 bluefoxdev.kr의 Bun 런타임 실전 가이드 를 참고하여 작성했습니다.


1. Bun vs Node.js 성능 비교

[벤치마크 (2026년 기준)]

HTTP 처리량 (간단한 JSON 응답):
  Bun.serve():    ~180,000 req/s
  Node.js HTTP:    ~60,000 req/s
  Hono on Bun:   ~170,000 req/s
  Express.js:     ~45,000 req/s

시작 시간:
  Bun:     ~5ms
  Node.js: ~50ms

패키지 설치 (node_modules 없는 상태):
  bun install:  ~0.5s
  npm install:  ~8s
  pnpm install: ~3s

번들링 (중형 앱):
  bun build:    ~200ms
  esbuild:      ~300ms
  webpack:      ~5,000ms

[Bun 내장 기능]
  번들러: bun build (esbuild 수준)
  테스트: bun test (Jest 호환)
  패키지 매니저: bun install (npm 호환)
  SQLite: bun:sqlite (내장, 외부 의존성 없음)
  .env 로딩: 자동 (dotenv 불필요)
  TypeScript: 트랜스파일 내장 (ts-node 불필요)

[Node.js 호환성 주의사항]
  __dirname, __filename: 지원
  Worker Threads: 지원
  Node.js crypto: 지원
  일부 네이티브 애드온: 미지원 가능
  node: 프로토콜 접두사: 지원

2. Bun HTTP 서버와 내장 기능 활용

// HTTP 서버 - Bun.serve()
const server = Bun.serve({
  port: 3000,
  
  async fetch(req) {
    const url = new URL(req.url);
    
    if (url.pathname === '/api/users' && req.method === 'GET') {
      const users = await db.query('SELECT * FROM users').all();
      return Response.json(users);
    }
    
    if (url.pathname === '/api/users' && req.method === 'POST') {
      const body = await req.json();
      const result = db.query('INSERT INTO users (name, email) VALUES (?, ?) RETURNING *')
        .get(body.name, body.email);
      return Response.json(result, { status: 201 });
    }
    
    return new Response('Not Found', { status: 404 });
  },
  
  error(error) {
    return Response.json({ error: error.message }, { status: 500 });
  },
});

console.log(`서버 실행: http://localhost:${server.port}`);

// 내장 SQLite - 외부 패키지 없음
import { Database } from 'bun:sqlite';

const db = new Database('app.db');

// 자동 타입 추론
db.run(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  )
`);

// 준비된 쿼리 (성능 최적화)
const getUserById = db.query<{ id: number; name: string; email: string }, [number]>(
  'SELECT id, name, email FROM users WHERE id = ?'
);

const user = getUserById.get(1);  // 타입 안전
// 파일 I/O - Bun의 빠른 파일 API
const file = Bun.file('data.json');
const data = await file.json();  // fs 불필요

await Bun.write('output.json', JSON.stringify(data, null, 2));

// 스트리밍 파일 읽기
const stream = Bun.file('large.csv').stream();
for await (const chunk of stream) {
  processChunk(chunk);
}

// .env 자동 로딩 (dotenv 불필요)
// .env 파일이 있으면 자동으로 process.env에 로드
console.log(process.env.DATABASE_URL);  // 바로 사용

// WebSocket 서버
const wsServer = Bun.serve({
  port: 3001,
  fetch(req, server) {
    if (server.upgrade(req)) return;  // WebSocket 업그레이드
    return new Response('WebSocket 서버', { status: 200 });
  },
  websocket: {
    open(ws) {
      console.log('클라이언트 연결');
      ws.subscribe('chat');  // 채널 구독
    },
    message(ws, message) {
      ws.publish('chat', message);  // 전체 브로드캐스트
    },
    close(ws) {
      ws.unsubscribe('chat');
    },
  },
});
// bun test - Jest 호환 테스트 러너
import { describe, it, expect, mock, beforeEach } from 'bun:test';

describe('UserService', () => {
  let mockDb: ReturnType<typeof mock>;
  
  beforeEach(() => {
    mockDb = mock(() => ({ id: 1, name: 'Alice', email: 'alice@example.com' }));
  });
  
  it('사용자를 생성해야 한다', async () => {
    const result = await createUser({ name: 'Alice', email: 'alice@example.com' });
    expect(result.name).toBe('Alice');
    expect(result.id).toBeNumber();
  });
  
  it('중복 이메일을 거부해야 한다', async () => {
    await expect(
      createUser({ name: 'Bob', email: 'alice@example.com' })
    ).rejects.toThrow('이미 존재하는 이메일');
  });
});

// package.json 스크립트 대신 bun 직접 사용
// bun run dev → tsx 대신 bun으로 직접 실행
// bun test → jest 대신 내장 테스트 러너
// bun build src/index.ts --outdir dist → 번들링
# Node.js → Bun 마이그레이션 체크리스트

# 1. Bun 설치
curl -fsSL https://bun.sh/install | bash

# 2. 의존성 설치 (bun install은 package.json 호환)
bun install

# 3. 스크립트 실행 (ts-node/tsx 대체)
# 기존: npx ts-node src/index.ts
# Bun: bun src/index.ts

# 4. 개발 서버 (nodemon 대체)
# 기존: nodemon src/index.ts
# Bun: bun --hot src/index.ts  (핫 리로드)

# 5. 호환성 확인
bun --bun src/index.ts  # Bun API 우선 사용

# 6. 성능 벤치마크
bun ./benchmarks/http.ts

# Node.js 전용 패키지 확인 (native addon)
# node-gyp 의존성은 별도 확인 필요
grep -r "node-gyp\|bindings\|nan\|node-addon-api" node_modules/.bin/

마무리

Bun은 2026년 현재 실제 프로덕션에서 사용할 수 있는 수준이다. API 서버, CLI 도구, 스크립트 실행에서 즉각적인 성능 향상을 경험할 수 있다. 주의할 점은 네이티브 애드온(bcrypt, sharp 등)의 호환성이며, 이 경우 해당 패키지의 Bun 지원 여부를 먼저 확인해야 한다. 새 프로젝트라면 Bun을 기본으로 선택하고, 기존 Node.js 앱은 핵심 경로부터 점진적으로 전환하는 전략이 안전하다.