Recent Posts
Recent Comments
반응형
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

오늘도 공부

workerd: Cloudflare Workers를 로컬과 프로덕션으로 확장하는 JavaScript/Wasm 런타임 본문

AI

workerd: Cloudflare Workers를 로컬과 프로덕션으로 확장하는 JavaScript/Wasm 런타임

행복한 수지아빠 2026. 3. 18. 09:48
반응형

AI Agent, 실시간 API, 엣지 애플리케이션이 점점 많아질수록 개발자는 한 가지 질문과 자주 마주칩니다.
“서버는 가볍고 빨라야 하는데, 동시에 브라우저처럼 익숙한 API를 쓰고 싶다. 그런데 Node.js나 컨테이너 기반 모델로는 너무 무겁다.”

workerd는 바로 그 지점에서 등장한 프로젝트입니다.
이건 단순한 “또 하나의 자바스크립트 런타임”이 아닙니다. Cloudflare Workers를 실제로 구동하는 핵심 런타임 코드를 바탕으로, 같은 실행 모델을 로컬 개발, 셀프 호스팅, 프록시 서버, 테스트 환경까지 확장하려는 시도에 가깝습니다. (GitHub)

개발자 관점에서 보면 더 흥미롭습니다. workerd는 브라우저 표준에 가까운 API, V8 isolate 기반의 경량 실행 모델, 서비스 간 바인딩, 호환성 날짜 기반 버전 전략까지 묶어서 **“서버 런타임을 어떻게 더 안전하고 빠르게 설계할 것인가”**에 대한 Cloudflare의 답을 보여줍니다. (GitHub)

 

 

GitHub - cloudflare/workerd: The JavaScript / Wasm runtime that powers Cloudflare Workers

The JavaScript / Wasm runtime that powers Cloudflare Workers - cloudflare/workerd

github.com

 


프로젝트 소개

workerd는 Cloudflare가 공개한 오픈소스 런타임으로, Cloudflare Workers를 구동하는 동일 계열의 코드 위에서 동작하는 JavaScript / Wasm 서버 런타임입니다. Cloudflare는 이 런타임을 애플리케이션 서버로 셀프 호스팅할 수도 있고, 로컬 개발 도구로 사용할 수도 있으며, 요청을 가로채고 라우팅하는 프로그래머블 HTTP 프록시로도 사용할 수 있다고 설명합니다. 저장소 기준 최신 릴리스는 2026년 3월 17일자 v1.20260317.1이며, Apache 2.0 라이선스로 배포됩니다. (GitHub)

기술 스택도 분명합니다. 저장소 기준 주요 구현 언어는 C++가 중심이고, JavaScript와 TypeScript가 큰 비중을 차지합니다. 빌드는 Bazel 기반이며, 런타임 구성은 일반적인 YAML이나 JSON이 아니라 Cap’n Proto 텍스트 포맷으로 정의합니다. 이 선택은 단순히 설정 문법이 독특하다는 의미를 넘어서, workerd가 “웹서버 + 서비스 그래프 + 바인딩 시스템”을 명시적으로 다루는 런타임이라는 점을 보여줍니다. (GitHub)

중요한 점도 하나 있습니다. workerd 자체는 Cloudflare Workers 서비스 전체가 아닙니다. Cloudflare는 공식적으로 workerd가 Workers 플랫폼의 핵심 런타임이지만, 실제 상용 서비스에는 배포 시스템, 오케스트레이션, 추가 보안 계층 등 더 많은 요소가 포함된다고 밝히고 있습니다. 즉, workerd는 Workers의 엔진에 가깝지, Cloudflare의 완성형 플랫폼 전체를 그대로 가져온 것은 아닙니다. (The Cloudflare Blog)


왜 이 프로젝트가 등장했을까

이 프로젝트를 이해하려면 먼저 기존 서버리스와 로컬 개발의 한계를 봐야 합니다.

전통적인 서버리스나 컨테이너 기반 실행 모델은 요청마다 프로세스나 컨테이너 단위의 오버헤드를 감수해야 합니다. 반면 Cloudflare Workers는 V8 isolate를 사용해 한 런타임 인스턴스 안에서 수백~수천 개의 격리된 실행 컨텍스트를 운영할 수 있고, 이 모델은 프로세스 기반 접근보다 훨씬 빠른 시작 시간과 더 낮은 메모리 사용량을 목표로 합니다. Cloudflare 문서도 isolate가 컨테이너/VM보다 훨씬 가볍고, 코드 격리와 빠른 시작을 동시에 제공한다고 설명합니다. (Cloudflare Docs)

두 번째 배경은 로컬 개발의 정확도입니다. Cloudflare는 과거 로컬 테스트를 위해 Miniflare를 사용했지만, Node.js 위에서 Workers API를 시뮬레이션하는 방식은 실제 프로덕션 런타임과 완전히 같을 수 없었습니다. 그래서 workerd 공개 이후에는 Wrangler와 Miniflare가 더 정확한 로컬 실행 환경을 제공할 수 있도록, 실제 프로덕션과 같은 계열의 런타임 코드를 활용하는 방향으로 진화했습니다. 현재 Cloudflare 문서도 로컬 개발이 Miniflare를 통해 가능하며, 그 실행 기반이 workerd라고 명시합니다. (The Cloudflare Blog)

세 번째는 표준 기반 서버 런타임에 대한 수요입니다. workerd는 Node.js 중심의 서버 API가 아니라 fetch(), Request, Response 같은 웹 표준 API를 중심에 둡니다. 이건 브라우저와 서버의 프로그래밍 모델을 더 가깝게 만들고, 특정 플랫폼 종속성을 낮추려는 전략입니다. Cloudflare는 Workers가 V8 위에서 동작하면서 최신 브라우저 API 상당수를 구현한다고 설명하고, workerd 역시 표준 기반 설계를 핵심 원칙으로 내세웁니다. (GitHub)

결국 workerd는 이런 문제의식 위에서 등장했습니다.

기존 방식은 이랬습니다.

  • 로컬에서는 “비슷하게” 시뮬레이션한다
  • 프로덕션에서는 완전히 다른 런타임이 돈다
  • 서비스 간 연결은 네트워크 주소와 환경변수에 의존한다
  • 확장은 쉽지만, 호출 비용과 운영 복잡성이 커진다

workerd의 접근은 다릅니다.

  • 로컬과 프로덕션의 실행 모델을 최대한 맞춘다
  • 서비스를 네트워크가 아니라 capability binding으로 연결한다
  • 브라우저형 API로 서버를 작성하게 만든다
  • isolate 기반으로 가볍게 다수의 서비스를 수용한다 (GitHub)

핵심 기능

1. Cloudflare Workers와 최대한 같은 실행 모델

workerd의 가장 큰 강점은 “Cloudflare Workers와 비슷한 개발 경험”이 아니라, 실제로 같은 계열의 런타임 코드를 사용한다는 점입니다. 그래서 로컬 테스트, 셀프 호스팅, 프레임워크 통합에서 동작 차이를 줄이기 좋습니다. Cloudflare의 Vite 플러그인 문서도 로컬 코드가 workerd 내부에서 실행되어 프로덕션 동작과 최대한 가깝다고 설명합니다. (The Cloudflare Blog)

개발자 입장에서는 이게 중요합니다. “내 로컬에서는 됐는데 배포 후 깨진다” 같은 문제는 대개 런타임 차이에서 발생합니다. workerd는 그 차이를 줄이기 위한 매우 직접적인 답입니다.


2. 웹 표준 API 중심의 서버 프로그래밍

workerd는 서버 런타임이지만, 사고방식은 브라우저에 가깝습니다. 기본 진입점은 fetch()이고, 데이터 흐름은 Request와 Response 중심입니다. 아래 같은 코드는 브라우저나 Service Worker 스타일에 익숙한 개발자라면 거의 즉시 이해할 수 있습니다. (Cloudflare Docs)

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    if (url.pathname === "/health") {
      return new Response("ok");
    }

    return new Response(JSON.stringify({
      runtime: "workerd",
      path: url.pathname
    }), {
      headers: { "content-type": "application/json" }
    });
  }
}

이 방식의 장점은 단순합니다.
Node 전용 개념보다 웹 표준을 먼저 익히게 만들기 때문에, 프론트엔드와 백엔드의 경계가 얇아집니다.


3. Capability Binding 기반 서비스 연결

workerd README가 가장 강조하는 설계 중 하나가 capability binding입니다. 쉽게 말해, 코드가 전역 네트워크 권한을 갖는 대신 설정된 리소스와 서비스에만 접근하도록 만드는 구조입니다. 저장소의 workerd.capnp를 보면 서비스 바인딩, Durable Object 네임스페이스, KV, R2, Queue, Analytics Engine, Hyperdrive, 메모리 캐시, worker loader 같은 다양한 바인딩 타입이 정의되어 있습니다. (GitHub)

이건 단순한 DI 패턴이 아닙니다.
Cloudflare는 이 모델이 조합 가능성을 높이고 SSRF 공격에 면역적인 구조를 만든다고 설명합니다. 즉, 서비스 간 호출을 “URL 문자열 조합”이 아니라 명시적 권한 연결로 바꾸는 셈입니다. (GitHub)

예를 들어 개발자는 이렇게 생각할 수 있습니다.

export default {
  async fetch(request, env) {
    // env.AUTH_SERVICE 처럼 명시적으로 주입된 서비스만 접근
    const authResponse = await env.AUTH_SERVICE.fetch(
      new Request("https://service.internal/check", {
        method: "POST",
        body: JSON.stringify({ token: request.headers.get("authorization") })
      })
    );

    if (!authResponse.ok) {
      return new Response("unauthorized", { status: 401 });
    }

    return new Response("authorized");
  }
}

여기서 핵심은 AUTH_SERVICE가 “아무 URL로나 나갈 수 있는 클라이언트”가 아니라는 점입니다.
설정 파일이 연결해 준 서비스만 호출하게 만들어집니다.


4. Nanoservices 아키텍처

workerd는 마이크로서비스보다 더 잘게 쪼개진 nanoservices라는 표현을 씁니다. README 설명대로 각 서비스는 독립적으로 분리되지만, 서로 호출할 때 같은 프로세스와 스레드 안에서 실행될 수 있어 로컬 함수 호출에 가까운 성능 모델을 지향합니다. 또한 서로 다른 서비스들을 클러스터 내 개별 머신에 나눠 배포하는 대신, 모든 머신에 동일한 서비스 세트를 올리는 homogeneous deployment 개념도 함께 제시합니다. (GitHub)

이 설계는 특히 다음 상황에서 강합니다.

  • 인증, 라우팅, 변환, 캐시, 정책 검사를 작은 서비스로 분리하고 싶을 때
  • 네트워크 홉 없이 서비스 경계를 유지하고 싶을 때
  • 서비스 디스커버리와 복잡한 L4/L7 라우팅을 줄이고 싶을 때

즉, workerd는 “모놀리식 서버를 잘게 나누되, 네트워크 비용까지 같이 늘리고 싶지는 않다”는 요구에 꽤 흥미로운 답을 줍니다.


5. Compatibility Date 기반의 하위 호환 전략

대부분의 런타임 업그레이드는 개발자에게 스트레스입니다.
버전을 올리면 API 동작이 바뀌고, 라이브러리가 깨지고, 어느 순간 “언제부터 이게 바뀌었지?”라는 질문이 생깁니다.

workerd는 이 문제를 compatibility date로 풉니다. README와 스키마 문서에 따르면 워커는 compatibilityDate를 지정해야 하며, 최신 workerd 버전으로 올려도 과거 날짜를 지정하면 당시 API 동작을 에뮬레이션할 수 있습니다. 저장소 릴리스 이름이 날짜 형태인 것도 이 전략과 연결됩니다. (GitHub)

예시는 이런 식입니다.

const mainWorker :Workerd.Worker = (
  serviceWorkerScript = embed "hello.js",
  compatibilityDate = "2026-03-17",
);

이 모델은 플랫폼 팀과 애플리케이션 팀 모두에게 좋습니다.
플랫폼은 런타임을 계속 개선할 수 있고, 애플리케이션은 자신이 올릴 시점을 통제할 수 있기 때문입니다.


6. Node.js 호환성 확장

workerd가 웹 표준 중심이라고 해서 Node 생태계를 완전히 포기한 건 아닙니다. Cloudflare 문서에 따르면 Workers 런타임은 nodejs_compat 플래그를 통해 다양한 Node.js API를 기본 구현 또는 polyfill 형태로 제공합니다. Buffer, Crypto, HTTP, Path, Process, Stream 등은 지원되며, 일부 API는 부분 지원 또는 비기능 스텁 상태입니다. (Cloudflare Docs)

즉, workerd는 철학적으로는 웹 표준 우선이지만, 현실적으로는 npm 생태계를 끌어오기 위해 Node 호환 계층도 계속 확장하고 있습니다.

설정 예시는 다음처럼 이해하면 됩니다.

{
  "compatibility_flags": ["nodejs_compat"],
  "compatibility_date": "2026-03-17"
}

이건 “Node.js처럼 완전히 동작한다”가 아니라,
Workers 실행 모델 안에서 필요한 만큼 Node 생태계를 가져온다에 더 가깝습니다.


프로젝트 아키텍처 분석

workerd는 겉으로 보면 단순한 서버 바이너리지만, 내부 개념은 꽤 구조적입니다.

  1. 소켓이 들어온 요청을 받습니다.
  2. 소켓은 특정 service로 연결됩니다.
  3. service는 Worker일 수도 있고, 다른 리소스 서비스일 수도 있습니다.
  4. Worker는 isolate 안에서 실행됩니다.
  5. Worker는 env binding을 통해 다른 서비스나 저장소에 접근합니다.
  6. 필요한 경우 Durable Objects, KV, R2, Queue 같은 바인딩이 내부적으로 서비스 호출 또는 저장소 접근으로 이어집니다. (GitHub)

이를 다이어그램으로 단순화하면 아래와 같습니다.

조금 더 개발자 시점으로 풀어보면 이렇습니다.

소켓과 서비스

workerd 설정 파일에서 가장 위쪽 레벨은 대체로 services와 sockets입니다.
서비스는 이름을 가진 실행 단위이고, 소켓은 외부 요청이 어느 서비스로 들어가는지 연결합니다. 샘플 helloworld 설정도 정확히 이 모델을 따릅니다. (GitHub)

using Workerd = import "/workerd/workerd.capnp";

const config :Workerd.Config = (
  services = [
    (name = "main", worker = .mainWorker),
  ],

  sockets = [
    (
      name = "http",
      address = "*:8080",
      http = (),
      service = "main"
    ),
  ]
);

const mainWorker :Workerd.Worker = (
  serviceWorkerScript = embed "hello.js",
  compatibilityDate = "2026-03-17",
);

이 구성이 의미하는 건 단순합니다.
포트 8080으로 들어온 HTTP 요청을 main 서비스에 연결하고, 그 서비스는 mainWorker를 실행한다는 뜻입니다.

Worker와 isolate

실제 코드 실행은 V8 isolate 안에서 이뤄집니다. Cloudflare 문서는 한 런타임 인스턴스가 많은 isolate를 호스팅할 수 있고, 각 isolate는 메모리가 분리된 안전한 실행 컨텍스트라고 설명합니다. 이 모델은 컨테이너를 매 요청 띄우는 방식보다 훨씬 가볍고 빠른 스타트업 비용을 노립니다. (Cloudflare Docs)

Binding과 내부 서비스 호출

env로 주입되는 각종 자원은 단순한 환경변수가 아니라 capability입니다.
서비스 바인딩은 다른 Worker 서비스 호출로 이어지고, KV/R2/Queue 같은 바인딩도 내부적으로는 지정된 서비스로 요청을 전달하는 식으로 모델링됩니다. 저장소의 스키마 주석은 KV namespace, R2 bucket, Queue binding이 지정된 서비스로 변환된 HTTP 요청 형태로 연결된다고 설명합니다. (GitHub)

이 지점이 workerd의 설계 철학을 잘 보여줍니다.
“모든 것을 네트워크 주소로 푸는 대신, 서비스 그래프로 명시한다.”

Durable Objects와 상태 저장

상태가 필요한 경우에는 Durable Objects가 핵심 역할을 합니다. Cloudflare 문서에 따르면 Durable Objects는 조정이 필요한 상태 저장 애플리케이션을 위한 빌딩 블록이고, 최신 스토리지 모델은 SQLite 기반입니다. workerd.capnp에도 Durable Object의 스토리지를 메모리, 로컬 디스크, SQLite 관련 옵션 등으로 정의하는 구조가 보이며, SQL API 활성화 플래그도 존재합니다. (Cloudflare Docs)


직접 써보면 어떻게 보일까

가장 간단한 예시는 hello world입니다.

worker.js

addEventListener("fetch", event => {
  event.respondWith(
    new Response("Hello from workerd")
  );
});

config.capnp

using Workerd = import "/workerd/workerd.capnp";

const helloWorld :Workerd.Config = (
  services = [
    (name = "main", worker = .app)
  ],

  sockets = [
    (name = "http", address = "*:8080", http = (), service = "main")
  ]
);

const app :Workerd.Worker = (
  serviceWorkerScript = embed "worker.js",
  compatibilityDate = "2026-03-17"
);

실행은 README 기준으로 아래처럼 할 수 있습니다. 빌드한 바이너리를 직접 쓰거나, npm 배포 바이너리를 npx workerd로 실행할 수 있습니다. (GitHub)

workerd serve config.capnp

이 단순한 구조만 봐도 workerd가 Express 같은 앱 프레임워크가 아니라,
서비스와 소켓을 조립하는 런타임이라는 점이 드러납니다.


개발자에게 특히 흥미로운 사용 시나리오

1. Cloudflare Workers 앱을 로컬에서 더 정확하게 테스트하고 싶을 때

Workers용 코드를 짜고 있는데, Node 기반 모킹이 점점 부담스럽다면 workerd는 매우 좋은 선택입니다. 특히 Wrangler, Miniflare, Cloudflare Vite 플러그인과 이어지는 생태계는 “로컬에서도 실제 런타임에 가깝게”라는 목표를 분명히 갖고 있습니다. (GitHub)

2. 웹 표준 기반의 경량 API 서버를 만들고 싶을 때

Express나 Fastify보다 더 낮은 레벨에서, fetch 중심 서버를 선호한다면 workerd는 독특한 대안입니다. 특히 브라우저 API에 익숙한 팀에게는 러닝 커브가 의외로 낮습니다. (GitHub)

3. 서비스 간 네트워크 홉을 줄이고 싶은 내부 플랫폼 팀

nanoservices와 service binding 모델은 사내 플랫폼, API 게이트웨이, 정책 엔진, 인증 계층처럼 “작게 나누고 싶지만 너무 많은 네트워크 호출은 싫다”는 경우에 잘 맞습니다. README가 programmable HTTP proxy 용도를 직접 언급하는 것도 같은 맥락입니다. (GitHub)

4. Durable Objects 기반 상태 저장 앱을 실험하고 싶을 때

채팅, 협업 문서, 게임 룸, 세션 코디네이션, AI 에이전트 상태 관리처럼 단순 KV 이상이 필요한 경우 Durable Objects는 매우 강력한 조합입니다. 최근 Cloudflare는 SQLite-backed Durable Objects를 적극적으로 확장하고 있고, workerd는 이 모델을 이해하는 데 가장 좋은 출발점 중 하나입니다. (Cloudflare Docs)


workerd를 볼 때 주의할 점

이 프로젝트를 볼 때 가장 흔한 오해는 이것입니다.

“그럼 workerd만 쓰면 Cloudflare Workers 전체를 내 서버에서 그대로 돌릴 수 있겠네?”

완전히는 아닙니다.
Cloudflare도 workerd가 Workers 서비스 전체가 아니라, 그중 핵심 런타임 부분이라고 명확히 선을 긋습니다. 배포 파이프라인, 멀티테넌시 하드닝, 운영 보안, 글로벌 오케스트레이션은 별개의 문제입니다. 게다가 README는 workerd 자체가 hardened sandbox가 아니며, 신뢰할 수 없는 코드를 실행할 때는 VM 같은 추가 보안 경계 안에서 돌려야 한다고 경고합니다. (The Cloudflare Blog)

즉, workerd는 훌륭한 런타임이지만, 멀티테넌트 코드 실행 플랫폼을 바로 만드는 만능 키트는 아닙니다.


한 줄로 정리하면

workerd는 Cloudflare Workers의 아이디어를 오픈소스 런타임으로 꺼내온 프로젝트입니다.
더 정확히 말하면, 웹 표준 API + V8 isolate + capability binding + compatibility date라는 조합으로 현대 서버 런타임을 다시 설계한 결과물에 가깝습니다. (GitHub)

개발자에게 이 프로젝트가 중요한 이유는 분명합니다.

Node.js의 대체재라서가 아닙니다.
서버를 브라우저처럼 프로그래밍하면서도,
서비스를 네트워크 주소가 아니라 명시적 권한으로 연결하고,
상태 저장 컴퓨팅까지 같은 런타임 모델 안으로 끌어들이려는 방향성을 보여주기 때문입니다. (The Cloudflare Blog)

workerd는 지금도 계속 진화 중입니다.
하지만 이미 충분히 분명한 메시지를 던지고 있습니다.

앞으로의 서버 런타임은 더 무거운 VM이 아니라, 더 가벼운 isolate와 더 명시적인 바인딩 위에서 움직일 수 있다.

반응형