들어가며: "다른 언어끼리 RPC? 그거 원래 빡센 거 아닌가요?"

마이크로서비스를 운영해본 분이라면 공감하실 텐데요. 서로 다른 언어로 작성된 서비스끼리 통신하려면 보통 이런 삽질이 필요했어요.

  • Protobuf, Thrift 같은 IDL을 별도로 정의하고
  • 스키마 변경할 때마다 **코드 생성(Codegen)**을 다시 돌리고
  • 언어별로 직렬화/역직렬화 레이어를 각각 관리

Cloudflare가 이번에 공개한 크로스랭귀지 RPC는 이 흐름을 완전히 뒤집습니다. Python Worker에서 TypeScript Worker의 메서드를 그냥 await rpc.add(42, 144) 한 줄로 호출할 수 있어요. 스키마도, 의존성 추가도, 별도 빌드 스텝도 필요 없습니다.

이 글에서는 이 기능이 어떻게 가능한지, 실무에서 어떻게 쓸 수 있는지, 그리고 어디서 주의해야 하는지를 정리해볼게요. 👉 근거자료

Server architecture diagram showing cross-language RPC communication between Python and JavaScript Workers Dev Environment Setup

핵심: 스키마 없이 언어를 넘나드는 RPC

TypeScript Worker에서 메서드 정의

import { WorkerEntrypoint } from "cloudflare:workers";

export class RpcService extends WorkerEntrypoint {
  async add(a: number, b: number): Promise<number> {
    // 두 수를 더해서 반환합니다.
    return a + b;
  }
}

Python Worker에서 그대로 호출

from workers import Response, WorkerEntrypoint

class Default(WorkerEntrypoint):
    async def fetch(self, request):
        # TypeScript Worker의 RPC 스텁을 가져옵니다.
        rpc = self.env.RPC
        # TypeScript의 RPC 메서드를 그대로 호출합니다.
        result = await rpc.add(42, 144)
        return Response.json({"result": result})

서비스 바인딩만 추가하면 끝

"services": [
  {
    "binding": "RPC",
    "service": "ts-rpc-server",
    "entrypoint": "RpcService"
  }
]

네, 이게 전부입니다. Protobuf도, 스키마 코드 생성도, 별도 SDK 설치도 필요 없어요.

내부에서는 무슨 일이 벌어지나?

핵심은 Pyodide의 FFI(Foreign Function Interface) 입니다. Python Worker는 CPython을 WebAssembly로 컴파일한 Pyodide 위에서 돌아가는데, 이 FFI가 JavaScript와 Python 사이의 타입을 자동으로 변환해줘요.

Python 타입JavaScript 타입
int, floatNumber
boolBoolean
dictObject
listArray
datetimeDate

직접 변환이 안 되는 커스텀 클래스나 함수는 Proxy 객체로 감싸서 속성 접근과 메서드 호출을 그대로 전달합니다. 예를 들어 Python 함수를 JavaScript 콜백으로 넘기는 것도 자연스럽게 동작해요.

키워드 인자(Keyword Arguments)도 똑똑하게 매핑됩니다. JavaScript 쪽에서 get(key, options?) 형태로 정의된 메서드를 Python에서 JSRPC.get("myKey", type="text") 처럼 호출하면, 내부적으로 { type: "text" } 객체로 변환돼요. Python 개발자는 Python스럽게, JS 개발자는 JS스럽게 쓰면 됩니다.

Python and JavaScript code snippets side by side demonstrating Cloudflare Workers RPC method calls Developer Related Image

주의사항: Web API 객체는 별도 SDK가 필요해요

Pyodide FFI가 기본 타입은 다 알아서 변환해주지만, Request, Response, Blob, File 같은 Web API 객체는 자동 변환 대상이 아닙니다. 이건 Cloudflare Workers에서 자주 쓰이는 타입인데 Python에는 네이티브 대응 타입이 없거든요.

기본 동작은 이 객체들을 JavaScript Proxy로 감싸서 그대로 넘기는 것인데, 이러면 Python 코드 안에 JS 구현 세부사항이 새어 나와서 개발자 멘탈 모델이 지저분해집니다.

이 문제를 해결하려고 Cloudflare는 workers-runtime-sdk 라는 Python 패키지를 제공해요. uv run pywrangler deploy 로 배포하면 자동으로 포함되고, from workers import Response 처럼 workers 네임스페이스에서 import 하는 순간 이미 쓰고 있는 셈입니다.

이 SDK가 언어 경계를 넘는 객체를 가로채서 양쪽 언어 모두에서 자연스러운 네이티브 형태로 변환해줘요.

실전 예제: Python 패키지를 JavaScript에서 쓰기

JavaScript 앱에서 Pygments(파이썬 문법 하이라이터)를 쓰고 싶다면?

// JavaScript Worker: Python Worker의 메서드를 호출합니다.
export default {
  async fetch(request, env) {
    const rpc = env.PYTHON_RPC;
    const result = await rpc.highlight_code('print(42)', 'python');
    return Response.json(result);
  }
}
# Python Worker: Pygments를 사용해 하이라이팅을 수행합니다.
from workers import WorkerEntrypoint
from pygments.formatters import HtmlFormatter
from pygments import highlight
from pygments.lexers import get_lexer_by_name

class Default(WorkerEntrypoint):
    async def highlight_code(self, code: str, language: str) -> dict:
        # 지정한 언어의 렉서를 가져옵니다.
        lexer = get_lexer_by_name(language, stripall=True)
        # 포매터를 만들고 하이라이팅을 실행합니다.
        formatter = HtmlFormatter(linenos=True, cssclass="highlight", style="monokai")
        highlighted_html = highlight(code, lexer, formatter)
        # 스타일용 CSS를 가져옵니다.
        css = formatter.get_style_defs(".highlight")
        return {"html": highlighted_html, "css": css}

JS Worker 쪽 wrangler.jsonc에는 서비스 바인딩만 추가하면 됩니다.

"services": [
  { "binding": "PYTHON_RPC", "service": "py-rpc-server" }
]

이 기술의 한계와 주의사항

  • RPC는 대부분 네트워크를 넘지 않아요. 같은 스레드에서 돌기 때문에 성능 오버헤드는 거의 없지만, 그만큼 Worker 간 격리 수준이 낮다는 의미이기도 합니다. 장애 격리(Fault Isolation)가 필요하면 별도 설계가 필요해요.
  • Python Worker의 콜드 스타트는 Pyodide(WASM) 로딩 때문에 순수 JS Worker보다 느립니다. 지연 시간에 민감한 경로에는 부적합할 수 있어요.
  • Pyodide FFI가 못 잡는 커스텀 타입은 여전히 Proxy로 넘어가기 때문에, 예상치 못한 곳에서 JS 객체처럼 동작할 수 있습니다. 타입 힌트를 명확히 달아두는 습관이 도움이 돼요.
  • 양쪽 Worker를 별도 터미널에서 각각 dev 서버로 띄워야 해서 로컬 개발 경험이 아직 매끄럽지는 않습니다.

한국 개발 생태계에서의 적용 맥락

국내 SI·스타트업 환경에서는 레거시 Python 배치/ML 코드를 Node 기반 BFF 뒤에 붙이는 시나리오가 특히 유용할 것 같아요. 기존에는 FastAPI로 한 번 더 감싸고 HTTP로 붙이는 패턴이 흔했는데, Workers RPC는 그 중간 레이어를 통째로 제거할 수 있습니다. 다만 Cloudflare Workers 자체가 국내 리전 이슈가 있는 만큼, 지연 시간 요구사항이 엄격한 서비스라면 벤치마크를 먼저 해보시는 걸 권합니다.

Cloud infrastructure visualization of Cloudflare Workers runtime bridging Python and JavaScript execution

정리: "언어 경계는 이제 라이브러리 호출처럼"

Cloudflare Workers의 크로스랭귀지 RPC는 스키마·코드젠·직렬화 레이어 없이 Python과 JavaScript를 한 애플리케이션처럼 엮어주는 기능입니다. 정리하면:

  1. Service binding + 메서드 호출 만으로 언어를 넘나드는 RPC가 성립
  2. Pyodide FFI가 기본 타입을 자동 변환, 커스텀 타입은 Proxy로 전달
  3. workers-runtime-sdk 가 Web API 객체까지 자연스럽게 다뤄줌
  4. 대신 콜드 스타트, 격리 수준, 로컬 DX는 아직 트레이드오프가 존재

다음 단계 학습 방향

함께 보면 좋은 글

본 콘텐츠는 신뢰할 수 있는 출처를 바탕으로 AI 도구를 활용하여 초안이 작성되었으며, 편집자의 검토를 거쳐 발행되었습니다. 전문가의 조언을 대체하지 않습니다.