본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

Bun — 런타임과 개발 도구를 한 바이너리에

~11 min · tooling, bun, runtime

Level 0노드 입문자
0 XP0/40 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Bun은 런타임, 패키지 관리자, 번들러, 테스트 러너를 한 실행 파일에 넣었어. 장점은 한꺼번에 갈아타는 데 있지 않고, 필요한 역할부터 골라 쓸 수 있다는 데 있어."

Bun이 한곳에 모은 것

Bun은 JavaScriptCore를 사용하는 JavaScript 런타임이면서 개발 도구 묶음이야. bun script.js로 코드를 실행하고, bun install로 npm 레지스트리의 패키지를 설치하며, bun testbun build로 테스트와 번들링도 할 수 있어.

  1. 런타임은 Node용 코드와 Web API를 넓게 지원해.
  2. 패키지 관리자는 Node도 읽을 수 있는 node_modules를 만들고 현재 잠금 결과를 bun.lock에 기록해.
  3. 내장 도구는 TypeScript 실행, 테스트, 번들링, 감시 모드를 한 명령 체계로 제공해.

한 바이너리에 모였다고 모든 역할을 함께 써야 하는 건 아냐. 설치는 Bun으로 하고 실행은 Node로 유지하는 조합도 가능해.

Node 호환성은 목표이지 동일성은 아냐

Bun은 fs, http, node:crypto 같은 Node 내장 모듈과 fetch(), Web Streams 같은 Web API를 구현해. 널리 쓰이는 npm 패키지 상당수가 수정 없이 동작하지만, Node와 완전히 같은 런타임은 아니야.

네이티브 애드온, 일부 worker_threads 사용법, 세부적인 http2 동작처럼 차이가 드러날 수 있는 경계가 있어. 호환성 표를 믿는 데서 끝내지 말고 실제 프로젝트의 테스트와 배포 흐름을 Bun에서 돌려 봐야 해.

속도는 작업별로 재

Bun의 이점은 "항상 더 빠르다"가 아니라 특정 작업의 비용을 크게 줄일 수 있다는 데 있어.
  • 패키지 설치와 짧은 스크립트 시작은 눈에 띄게 빨라질 수 있어.
  • 단순한 HTTP 처리량도 높게 나오는 경우가 많지만, 실제 서비스는 데이터베이스나 외부 API가 병목일 수 있어.
  • 순수 계산 성능은 코드와 엔진 최적화에 따라 결과가 달라져.
  • 번들링은 esbuild 같은 빠른 전용 도구와 직접 재 보는 게 맞아.
"몇 퍼센트 빠르다"는 제목보다 아빠 코드의 설치 시간, 시작 시간, 요청 지연을 직접 재. 작업이 바뀌면 답도 바뀌어.

가장 작은 도입부터 시작해

  • 설치만 바꾸기: bun install로 의존성을 설치하고 앱은 node로 실행해.
  • 개발 스크립트에 쓰기: 짧은 TypeScript 도구를 bun run으로 실행해 별도 실행기를 줄여.
  • 시작 시간이 중요한 프로그램에 쓰기: CLI나 짧게 사는 작업에서 실제 이득을 재 봐.
  • 새 서비스의 런타임으로 쓰기: 필요한 Node API와 배포 환경이 모두 맞는지 테스트한 뒤 결정해.

오랫동안 운영한 Node 서비스라면 호환성 검증 비용이 설치 속도 이득보다 클 수 있어. 새 도구를 좋아하는 마음과 운영 위험을 같은 숫자로 비교해야 해.

Bun 전용 API는 선택을 더 깊게 만들어

Bun.serve(), Bun.file(), Bun.spawn() 같은 API는 간결하고 빠른 경로를 제공해. 대신 코드가 그 API에 기대기 시작하면 Node로 되돌아갈 때 바꿀 부분도 늘어나.

const server = Bun.serve({
  port: 3000,
  fetch(request) {
    return new Response('hi from Bun');
  },
});

const file = Bun.file('./data.json');
const json = await file.json();

const process = Bun.spawn(['ls', '-la']);
const output = await new Response(process.stdout).text();

여러 런타임을 오갈 가능성이 크다면 표준 Web API와 Node 호환 표면을 우선하고, Bun 전용 API가 실제로 값을 만드는 경계에서만 사용해.

Pippa의 고백

사이드 프로젝트 전체를 Bun으로 옮겼다가 Node 전용 의존성 하나에서 오래 막힌 적이 있어. 다시 Node로 돌아온 뒤 설치 단계만 Bun으로 바꾸니 위험은 거의 없고 기다리는 시간은 줄더라. 아빠가 "도구 하나를 골랐다고 모든 역할까지 충성할 필요는 없어"라고 했지. 이제는 설치, 스크립트, 테스트, 런타임을 따로 재고 필요한 자리만 바꿔.

Code

설치, 실행, 테스트, 번들링 명령·bash
# 패키지 설치
bun install
bun add react
bun add -d typescript
bun remove unused-pkg

# TypeScript 개발 스크립트 실행
bun run scripts/build.ts
bun run test
bun --watch script.ts

# 내장 테스트 러너
bun test
bun test --watch

# 내장 번들러
bun build src/index.ts --outfile=dist/index.js --target=node
Bun.serve와 Node HTTP 서버의 경계 비교·javascript
// Bun 전용 HTTP 서버
const server = Bun.serve({
  port: 3000,
  async fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === '/') return new Response('hi from Bun');
    if (url.pathname === '/data') {
      return new Response(Bun.file('./big.json'));
    }
    return new Response('not found', { status: 404 });
  },
});
console.log(`listening on http://localhost:${server.port}`);

// 같은 역할을 Node 내장 모듈로 구현
import { createServer } from 'node:http';
import { createReadStream } from 'node:fs';

createServer((req, res) => {
  if (req.url === '/') return res.end('hi from Node');
  if (req.url === '/data') {
    res.setHeader('Content-Type', 'application/json');
    return createReadStream('./big.json').pipe(res);
  }
  res.writeHead(404).end('not found');
}).listen(3000);

External links

Exercise

기존 Node 프로젝트 하나에서 설치 시간과 짧은 개발 스크립트 시작 시간을 재. 먼저 현재 패키지 관리자로 깨끗하게 설치하고, 다음에는 bun install로 같은 과정을 반복해. 앱은 Node와 Bun에서 각각 테스트를 돌려 보고, 설치만 Bun으로 바꿀지 런타임까지 바꿀지 측정 결과와 호환성 문제를 근거로 결정해.
Hint
기존 잠금 파일은 지우지 말고 별도 작업 트리나 복사본에서 비교해. 현재 Bun은 bun.lock을 사용하고 예전 bun.lockb는 이전 형식이야. 런타임을 바꿨을 때 실패하면 오류가 난 Node API를 Bun의 공식 호환성 문서에서 먼저 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 똘이warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.