---
title: "Webhooks 대 WebSockets 대 gRPC"
description: "실시간 데이터 전달을 지배하는 세 가지 프로토콜이 있습니다. webhooks, WebSockets, gRPC가 어떻게 다른지, 각각 언제 한계에 부딪히는지, 그리고 이들 중 어떻게 선택해야 하는지 설명합니다."
---

# Webhooks 대 WebSockets 대 gRPC

<ImageBlock
  src="https://media.alchemy.com/webhooks-vs-websockets-vs-grpc.png"
  alt="실시간 데이터 전송을 위한 webhooks, WebSockets, gRPC 비교 커버 이미지"
  width={1920}
  height={900}
  priority
/>

블록체인 데이터를 읽는 모든 애플리케이션은 동일한 아키텍처 질문에 직면합니다. 데이터가 시스템에 어떻게 도달해야 하는가? 몇 초마다 API를 폴링하면 컴퓨팅 자원을 낭비하고, 이벤트를 놓치고, 지연 시간이 늘어납니다. 대신 데이터를 푸시하려면 전달 모델을 선택해야 합니다.

실시간 데이터 전달을 지배하는 세 가지 프로토콜이 있습니다: [webhooks](https://www.alchemy.com/overviews/what-is-a-webhook), [WebSockets](https://www.alchemy.com/overviews/what-is-a-websocket), gRPC입니다. 이들은 서로 대체 가능한 것이 아닙니다. Webhooks는 무언가 발생했을 때 백엔드에 알립니다. WebSockets는 브라우저로 실시간 데이터를 스트리밍합니다. gRPC는 백엔드 서비스 간에 데이터를 높은 처리량으로 이동시킵니다. 잘못된 것을 선택하면 그 대가는 나중에 나타납니다: 놓친 이벤트, 낭비된 컴퓨팅 자원, 혹은 계획에 없던 시스템 재설계입니다.

## Webhooks, WebSockets, gRPC란 무엇인가?

세 가지 모두 폴링 없이 서버에서 애플리케이션으로 데이터를 이동시킵니다. 공통점은 여기까지입니다.

[webhook](https://www.alchemy.com/webhooks)은 일반적인 API 방향을 뒤집습니다. 코드가 서버를 호출하는 대신, 서버가 신경 쓰는 무언가가 발생할 때마다 등록해둔 URL을 JSON 페이로드와 함께 호출합니다. Webhooks는 연결하기 간단하지만, 대신 도착하는 데이터의 타이밍이나 형태를 제어할 수 없다는 트레이드오프가 있습니다.

[WebSocket](https://www.alchemy.com/smart-websockets)은 클라이언트와 서버 간의 지속적인 TCP 연결입니다. 어느 쪽이든 연결을 다시 맺을 필요 없이 언제든 작은 바이너리 프레임을 보낼 수 있습니다. 이 프로토콜은 HTTP 요청으로 시작해서 양쪽이 업그레이드에 동의하면 raw 소켓으로 전환됩니다.

gRPC는 원격 프로시저 호출(RPC) 프레임워크입니다. 한 서비스가 로컬 함수를 호출하듯 다른 서비스의 함수를 호출할 수 있게 해주는 시스템입니다. Google의 내부 RPC 시스템인 Stubby의 공개 후속작으로, gRPC는 HTTP/2 위에서 동작하고 Protocol Buffers로 데이터를 직렬화하며, 완전한 양방향을 포함한 4가지 스트리밍 모드를 지원합니다. 머신 간 구조화된 데이터를 빠르게 이동시키는 백엔드 서비스를 위해 만들어졌으며, 재시도, 데드라인 전파, 타입이 지정된 계약이 내장되어 있습니다.

<EmbeddedTable
  table={{
    columns: [
      { key: "dimension", width: 180, title: "Dimension", dataType: "object" },
      { key: "webhooks", width: 220, title: "Webhooks", dataType: "object" },
      { key: "websockets", width: 220, title: "WebSockets", dataType: "object" },
      { key: "grpc", width: 240, title: "gRPC", dataType: "object" },
    ],
    data: [
      {
        dimension: { title: "Protocol", tooltip: "", icon: "" },
        webhooks: { title: "HTTP POST callbacks", tooltip: "", icon: "" },
        websockets: { title: "Persistent TCP (<a href=\"https://www.rfc-editor.org/rfc/rfc6455.html\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 6455</a>)", tooltip: "", icon: "" },
        grpc: { title: "HTTP/2 + <a href=\"https://protobuf.dev/\" target=\"_blank\" rel=\"noopener noreferrer\">Protocol Buffers</a>", tooltip: "", icon: "" },
        id: 0,
      },
      {
        dimension: { title: "Direction", tooltip: "", icon: "" },
        webhooks: { title: "Server to receiver (one-way)", tooltip: "", icon: "" },
        websockets: { title: "Bidirectional", tooltip: "", icon: "" },
        grpc: { title: "Bidirectional (4 streaming modes)", tooltip: "", icon: "" },
        id: 1,
      },
      {
        dimension: { title: "Connection model", tooltip: "", icon: "" },
        webhooks: { title: "Stateless (new request per event)", tooltip: "", icon: "" },
        websockets: { title: "Stateful (persistent)", tooltip: "", icon: "" },
        grpc: { title: "Stateful (persistent, multiplexed)", tooltip: "", icon: "" },
        id: 2,
      },
      {
        dimension: { title: "Data format", tooltip: "", icon: "" },
        webhooks: { title: "JSON", tooltip: "", icon: "" },
        websockets: { title: "JSON or binary", tooltip: "", icon: "" },
        grpc: { title: "Protobuf (binary)", tooltip: "", icon: "" },
        id: 3,
      },
      {
        dimension: { title: "Per-message overhead", tooltip: "", icon: "" },
        webhooks: { title: "500-2,000 bytes (HTTP headers)", tooltip: "", icon: "" },
        websockets: { title: "2-6 bytes", tooltip: "", icon: "" },
        grpc: { title: "5-byte frame + compressed headers", tooltip: "", icon: "" },
        id: 4,
      },
      {
        dimension: { title: "Browser support", tooltip: "", icon: "" },
        webhooks: { title: "N/A (server-side)", tooltip: "", icon: "" },
        websockets: { title: "Native (99%+)", tooltip: "", icon: "" },
        grpc: { title: "Requires gRPC-Web proxy", tooltip: "", icon: "" },
        id: 5,
      },
      {
        dimension: { title: "Delivery guarantee", tooltip: "", icon: "" },
        webhooks: { title: "At-least-once (with retries)", tooltip: "", icon: "" },
        websockets: { title: "None built-in", tooltip: "", icon: "" },
        grpc: { title: "Configurable retries + deadlines", tooltip: "", icon: "" },
        id: 6,
      },
      {
        dimension: { title: "Best for", tooltip: "", icon: "" },
        webhooks: { title: "Event notifications", tooltip: "", icon: "" },
        websockets: { title: "Real-time browser UIs", tooltip: "", icon: "" },
        grpc: { title: "Service-to-service pipelines", tooltip: "", icon: "" },
        id: 7,
      },
    ],
  }}
/>

## Webhooks는 어떻게 동작하는가?

Webhooks는 API 모델을 뒤집습니다. 서버를 호출하는 것이 아니라 서버가 여러분을 호출합니다. 제공자에게 URL을 등록하고, 어떤 이벤트가 중요한지 알려준 다음 대기합니다. 무언가 발생하면 제공자가 여러분의 엔드포인트로 JSON을 POST하고 2xx 응답을 기대합니다.

<CodeSnippet
  language="javascript"
  code={`app.post('/webhook', (req, res) => {
  const sig = req.headers['x-alchemy-signature'];
  if (!verify(sig, req.rawBody, SECRET)) return res.status(401).end();
  queue.enqueue(req.body);
  res.status(200).end();
});`}
/>

위 스니펫은 webhook 핸들러입니다: 서명을 검증하고, 페이로드를 큐에 넣고, 200을 반환합니다. 위의 단순한 버전이 바로 대부분의 프로덕션 webhook 장애가 발생하는 지점이기도 합니다. 장난감 수준의 핸들러와 프로덕션용 핸들러를 가르는 것은 단순 버전이 잘못 처리하는 세 가지입니다: 서명 검증, 재시도 동작, 버스트 과부하입니다.

첫 번째 실패 모드는 취약한 서명 처리입니다. 제공자는 공유 비밀키를 사용해 모든 전송에 HMAC 서명을 합니다(Alchemy는 `X-Alchemy-Signature`, Stripe는 `Stripe-Signature`을 사용), 여러분의 핸들러는 HMAC을 재계산하고, 타이밍 공격을 피하기 위해 상수 시간으로 비교하고, 5분보다 오래된 타임스탬프를 가진 것은 거부해야 합니다. `verify()`를 호출하는 것은 한 줄이지만, 이를 올바르게 처리하는 것은 여러 단계입니다. 어느 한 단계라도 건너뛰면 여러분의 엔드포인트 URL을 아는 모든 프로세스를 신뢰하는 셈이 됩니다.

두 번째 실패 모드는 재시도 상황에서 멱등성이 없는 처리입니다. 전송이 실패하면(타임아웃, 부분 장애, 5xx 응답) 제공자는 지수 백오프로 재시도하며(보통 1초부터 지터를 두고 배로 늘어나 최대 1시간까지) 페이로드를 데드레터 큐로 보내기까지 수 시간에서 수일이 걸릴 수 있습니다. Webhooks는 최소 한 번(at-least-once) 전달을 보장하지, 정확히 한 번(exactly-once)을 보장하지 않습니다([exactly-once는 분산 시스템에서 불가능합니다](https://en.wikipedia.org/wiki/Two_Generals%27_Problem)), 따라서 중복 전송은 흔한 일입니다. 핸들러는 멱등적이어야 합니다: 동일한 이벤트를 두 번 처리해도 한 번 처리한 것과 같은 결과가 나와야 합니다. 이벤트 ID를 중복 제거 저장소에 기록하고 반복 전송에는 200을 반환하세요.

세 번째 실패 모드는 버스트 과부하이며, 대규모 상황에서 webhook 시스템을 무너뜨리는 원인입니다. 단일 온체인 이벤트가 수백만 건의 동시 전송을 유발할 수 있습니다. 여러분의 엔드포인트가 병목이 되면, 제공자의 재시도 큐가 여러분에 대한 DoS 공격자가 됩니다.

블록체인 인프라에서 webhooks는 이벤트 기반 워크플로우를 구동합니다. 저희 [Custom Webhooks](https://www.alchemy.com/docs/reference/notify-api-quickstart)는 30개 이상의 EVM 체인과 Solana에 걸쳐 주소 활동 모니터링, NFT 전송 알림, GraphQL 필터링된 스마트 컨트랙트 이벤트를 지원하며, 모두 HTTP POST 콜백으로 여러분의 엔드포인트에 전달됩니다.

## WebSockets는 어떻게 동작하는가?

WebSocket은 `Upgrade: websocket` 헤더를 가진 일반 HTTP 요청으로 시작합니다. 서버는 HTTP 101(Switching Protocols)로 응답하고, 이 시점부터 연결은 더 이상 HTTP로 통신하지 않습니다. 이 협상 과정이 WebSocket 핸드셰이크입니다. 그 이후에 남는 것은 그 위에 얇은 프레이밍 계층이 있는 raw TCP 소켓입니다: 메시지당 헤더 없음, 요청-응답 사이클 없음입니다.

HTTP를 제거하면 메시지당 비용이 극적으로 낮아집니다. 각 WebSocket 메시지는 [2-6바이트의 오버헤드](https://websocket.org/guides/websocket-protocol/)만 발생시킵니다(FIN 비트, opcode, 페이로드 길이). 모든 요청에 수백 바이트의 헤더가 포함되는 HTTP와 비교해 보세요. 초당 100개 메시지 기준으로, WebSocket 오버헤드는 초당 약 600바이트인 반면 동등한 HTTP 트래픽은 초당 60,000바이트입니다.

<CodeSnippet
  language="javascript"
  code={`const ws = new WebSocket('wss://eth-mainnet.g.alchemy.com/v2/KEY');
ws.send(JSON.stringify({
  jsonrpc: '2.0', id: 1, method: 'eth_subscribe',
  params: ['newHeads']
}));
ws.on('message', (data) => handleBlock(JSON.parse(data)));`}
/>

[WebSocket](https://www.alchemy.com/docs/reference/webhook-types) 연결은 상태를 유지하며 오래 지속됩니다. 어느 쪽이든 상대의 응답을 기다리지 않고 언제든 데이터를 보낼 수 있습니다. 서버는 주기적으로 ping 프레임을 보내고, 클라이언트는 pong 프레임으로 응답해 연결이 살아있음을 증명합니다. 이러한 하트비트가 없으면 죽은 연결("좀비")이 서버 자원을 낭비하고, 리버스 프록시가 30~120초 후 유휴 연결을 종료합니다.

트레이드오프는 이렇습니다: 여러분이 연결의 생명주기를 소유하게 되는데, 그 생명주기에는 처음 보이는 것보다 더 많은 것이 들어 있습니다. 연결이 끊기면 클라이언트는 재연결해야 하며, 지수 백오프로 재연결해야 합니다(재시도 사이 간격을 점진적으로 늘림) 그렇지 않으면 수천 개의 클라이언트가 동시에 재연결하면서 서버가 마비됩니다. 로드 밸런서 뒤에 두 개 이상의 WebSocket 서버를 운영할 경우, 로드 밸런서는 특정 클라이언트의 모든 재연결을 동일한 머신으로 라우팅해야 합니다. 그 머신이 클라이언트의 구독 상태를 보유하고 있기 때문입니다. 이 라우팅 규칙에는 이름이 있습니다: 세션 어피니티입니다. 그리고 한 서버에서 발생한 메시지가 다른 서버에 연결된 클라이언트에 도달해야 한다면, 서버들은 노드 간에 상태를 공유해야 합니다. WebSockets는 와이어 프로토콜을 제공할 뿐, 그 위의 모든 것은 여러분이 직접 구축해야 합니다.

블록체인 애플리케이션에서 [WebSockets](https://www.youtube.com/watch?v=hM1cf_7O2VY)는 실시간 이벤트 구독을 위한 표준 인터페이스입니다. Ethereum의 [eth_subscribe endpoint](https://www.alchemy.com/docs/reference/eth-subscribe)는 새 블록 헤더, 로그 이벤트, 대기 중인 트랜잭션을 지속적인 WebSocket 연결을 통해 푸시합니다. 저희 [Smart WebSockets](https://www.alchemy.com/smart-websockets)는 기본 프로토콜 위에 필터링된 구독과 자동 재연결 기능을 추가합니다.

브라우저로 실시간 데이터를 스트리밍하는 것은 WebSockets가 해결하기 위해 만들어진 문제입니다. WebSockets 이전에는 대안들(롱 폴링, server-sent events)이 메시지당 전체 HTTP 오버헤드를 지불하거나 한 방향으로만 동작했습니다. WebSockets는 raw TCP 소켓이 보통 존재할 수 없는 한 장소, 즉 브라우저에서, 지속적이고 오버헤드가 낮은 양방향 연결을 제공합니다.

그 명확함이 곧 한계이기도 합니다. WebSockets는 소비자가 브라우저가 아니고, 데이터가 구조화되어 있고, 볼륨이 많고, WebSockets가 애초에 약속한 적 없는 것들(타입이 지정된 스키마, 멀티플렉싱된 스트림, 데드라인 전파, 백프레셔)이 필요할 때 어려움을 겪습니다. 이것이 gRPC가 만들어진 이유입니다.

## gRPC는 어떻게 동작하는가?

gRPC는 설계 공간의 반대편 끝에서 출발합니다. Webhooks가 HTTP 콜백이고 WebSockets가 TCP 위의 얇은 프레이밍 계층이라면, gRPC는 타입이 지정된 계약을 핵심으로 하는 완전한 RPC 프레임워크입니다. 비즈니스 로직을 작성하기 전에 먼저 서비스와 그것이 주고받는 메시지를 정의하는 `.proto` 파일을 작성합니다. protobuf 컴파일러는 이 파일을 여러분의 언어로 된 생성 클라이언트/서버 코드(스텁이라고 부름)로 변환하므로, 코드에서 원격 메서드를 호출하는 것이 로컬 함수를 호출하는 것처럼 보입니다.

<CodeSnippet
  language="protobuf"
  code={`service Geyser {
  rpc Subscribe(stream SubscribeRequest)
    returns (stream SubscribeUpdate);
}`}
/>

전송 계층은 HTTP/2이며, 직렬화를 위해 Protocol Buffers와 짝을 이룹니다. 이 둘이 함께 일반적인 REST + HTTP/1.1 + JSON 스택보다 gRPC에 세 가지 이점을 줍니다:

- 멀티플렉싱은 head-of-line blocking을 제거합니다. 이는 HTTP/1.1의 문제로, 하나의 느린 응답이 동일한 연결의 이후 모든 요청을 지연시키는 것을 말합니다. gRPC에서는 각 호출이 같은 TCP 연결을 공유하는 독립적인 HTTP/2 스트림으로 실행됩니다. Square는 연결 오버헤드를 줄이기 위해 [내부 서비스 간 트래픽을 gRPC로 옮긴](https://grpc.io/about/) 여러 엔지니어링 팀 중 하나입니다.
- 헤더 압축(HPACK)은 반복되는 헤더를 압축된 참조로 대체합니다. 동일한 메서드, 콘텐츠 타입, 인증 헤더가 매 호출마다 반복되는 RPC 트래픽에서, 이는 [헤더 오버헤드를 85-90% 줄입니다](https://www.digitalocean.com/community/tutorials/http-1-1-vs-http-2-what-s-the-difference).
- Protocol Buffers는 메시지를 텍스트 대신 컴팩트한 바이너리로 직렬화합니다. 페이로드는 비압축 환경에서 JSON보다 34% 작으며, 역직렬화는 언어에 따라 [최대 6배 빠릅니다](https://auth0.com/blog/beating-json-performance-with-protobuf/). 트레이드오프는 바이너리 페이로드가 사람이 읽을 수 없으므로 트래픽을 확인하려면 grpcurl이나 Postman 같은 도구가 필요하다는 것입니다.

gRPC는 4가지 스트리밍 모드를 지원합니다. Unary(하나의 요청, 하나의 응답)는 표준 API 호출처럼 동작합니다. 서버 스트리밍(하나의 요청, 여러 응답)은 피드나 데이터 푸시에 적합합니다. 클라이언트 스트리밍(여러 요청, 하나의 응답)은 배치 업로드를 처리합니다. 양방향 스트리밍(양쪽이 독립적으로 전송)은 실시간, 상태를 가진 상호작용을 구동합니다.

gRPC는 범용 프로토콜이 아닙니다. 여러분이 양쪽 끝을 모두 제어하는 머신 간에 구조화된 데이터를 높은 속도로 이동시키는 백엔드 서비스에 좁게 최적화되어 있으며, 이 집중된 틈새 영역이 바로 스키마 작성과 코드 생성이라는 초기 비용이 보상받는 지점입니다. gRPC가 이 위치를 차지한 것은 단지 바이너리 인코딩 때문만이 아니라 프로토콜에 내장된 프로덕션 프리미티브 덕분입니다. [데드라인 전파](https://grpc.io/docs/guides/deadlines/)는 모든 타임아웃을 전체 호출 체인을 통과하는 절대적인 시점으로 바꿉니다. Service A가 5초 데드라인을 설정하고 Service B를 호출하고, Service B가 Service C를 호출한다면, 모든 홉이 정확히 남은 시간을 알 수 있습니다. 데드라인이 만료되면 모든 하위 작업이 취소되고 자원이 해제됩니다. [흐름 제어](https://grpc.io/docs/guides/flow-control/)는 HTTP/2로부터 상속받습니다: 느린 소비자가 따라가지 못할 때 송신자가 자동으로 속도를 맞춥니다. 이것이 내장된 백프레셔이며, webhooks도 WebSockets도 기본적으로 제공하지 않는 것입니다.

[Solana의 gRPC](https://www.alchemy.com/blog/introducing-alchemy-solana-grpc)는 사용 가능한 최고 성능의 데이터 스트리밍을 구동합니다. [Yellowstone gRPC plugin](https://www.alchemy.com/docs/reference/yellowstone-grpc-overview)(Geyser)은 밸리데이터의 메모리 공간에 직접 로드되어, 계정 및 트랜잭션 업데이트를 디스크에 기록되기 전에 캡처하고, protobuf로 직렬화한 다음, 지속적인 HTTP/2 스트림을 통해 푸시합니다.

## 세 프로토콜은 성능 측면에서 어떻게 비교되는가?

처리량과 지연 시간 벤치마크는 서비스 간 통신에서 명확한 선을 그립니다. 바이너리 protobuf 인코딩과 HTTP/2 멀티플렉싱의 조합은 REST over HTTP/1.1보다 측정 가능할 만큼 더 높은 처리량과 더 낮은 지연 시간을 gRPC에 제공하며, 특히 페이로드가 작고 동시 호출이 많은 워크로드에서 그렇습니다.

WebSockets는 다른 경쟁에서 승리합니다: 브라우저-서버 간 스트리밍에서 메시지당 오버헤드가 가장 낮습니다. 초기 핸드셰이크 이후, 각 메시지는 2-6바이트의 프레이밍 오버헤드만 발생시키며, 브라우저의 네이티브 WebSocket API가 프록시나 빌드 도구 없이 연결을 처리합니다.

Webhooks는 성능을 위해 설계되지 않았습니다. 각 전송은 전체 연결 설정 오버헤드를 가진 독립적인 HTTP 요청입니다. 이들은 다른 문제, 즉 처리량보다 단순함과 호환성이 더 중요한 신뢰성 있는 비동기 이벤트 알림을 해결합니다.

<EmbeddedTable
  table={{
    columns: [
      { key: "metric", width: 200, title: "Metric", dataType: "object" },
      { key: "webhooks", width: 220, title: "Webhooks", dataType: "object" },
      { key: "websockets", width: 220, title: "WebSockets", dataType: "object" },
      { key: "grpc", width: 260, title: "gRPC", dataType: "object" },
    ],
    data: [
      {
        metric: { title: "Latency per message", tooltip: "", icon: "" },
        webhooks: { title: "Highest (full HTTP round-trip)", tooltip: "", icon: "" },
        websockets: { title: "Low (2-6 byte frame)", tooltip: "", icon: "" },
        grpc: { title: "Lowest (binary + multiplexed)", tooltip: "", icon: "" },
        id: 0,
      },
      {
        metric: { title: "Throughput ceiling", tooltip: "", icon: "" },
        webhooks: { title: "Limited by HTTP overhead", tooltip: "", icon: "" },
        websockets: { title: "High for single stream", tooltip: "", icon: "" },
        grpc: { title: "Highest (multiplexed streams)", tooltip: "", icon: "" },
        id: 1,
      },
      {
        metric: { title: "Serialization cost", tooltip: "", icon: "" },
        webhooks: { title: "JSON only", tooltip: "", icon: "" },
        websockets: { title: "JSON or binary", tooltip: "", icon: "" },
        grpc: { title: "<a href=\"https://auth0.com/blog/beating-json-performance-with-protobuf/\" target=\"_blank\" rel=\"noopener noreferrer\">Protobuf: up to 6x faster than JSON</a>", tooltip: "", icon: "" },
        id: 2,
      },
      {
        metric: { title: "Connection setup", tooltip: "", icon: "" },
        webhooks: { title: "Per event (or connection reuse)", tooltip: "", icon: "" },
        websockets: { title: "Once, then persistent", tooltip: "", icon: "" },
        grpc: { title: "Once, then persistent + multiplexed", tooltip: "", icon: "" },
        id: 3,
      },
      {
        metric: { title: "Backpressure", tooltip: "", icon: "" },
        webhooks: { title: "None (receiver absorbs or fails)", tooltip: "", icon: "" },
        websockets: { title: "None built-in", tooltip: "", icon: "" },
        grpc: { title: "Built-in (HTTP/2 flow control)", tooltip: "", icon: "" },
        id: 4,
      },
    ],
  }}
/>

블록체인 관련 수치도 같은 패턴을 뒷받침합니다. Solana의 Yellowstone gRPC는 약 5ms의 슬롯 지연 시간으로 데이터를 스트리밍합니다. 네이티브 WebSockets는 약 10ms에 전달합니다. RPC 폴링은 약 150ms로 뒤처집니다. 프로토콜 복잡도가 한 단계 올라갈 때마다 측정 가능한 속도 향상을 얻습니다.

## 각 프로토콜은 어디서 한계에 부딪히는가?

Webhooks는 고빈도 이벤트에서 어려움을 겪습니다. 초당 수백 건의 전송에서 콜백당 HTTP 오버헤드가 병목이 됩니다. 버스트 이벤트는 thundering-herd 문제를 만듭니다: 주요 온체인 이벤트가 수백만 건의 webhook 전송을 동시에 유발하여 제공자의 재시도 큐와 소비자 엔드포인트를 모두 압도합니다. Webhooks는 또한 엄격하게 단방향입니다. 양방향 통신이 필요한 어떤 사용 사례든 다른 프로토콜이 필요합니다. webhooks 인프라를 구축한 한 엔지니어가 [이렇게 표현했습니다](https://brandur.org/webhooks): "webhooks는 운영하기 고통스럽다."

WebSockets는 규모가 커지면 어려움을 겪습니다. 각 연결은 서버 자원을 점유합니다(유휴 시 2-10 KB, 활성 시 더 많음). 튜닝된 서버는 [50만 개 이상의 유휴 연결](https://websocket.org/guides/connection-limits/)을 처리할 수 있지만, 진짜 병목은 연결 교체입니다: TLS 핸드셰이크는 CPU 코어당 초당 1,000-3,000건을 소모합니다. 서버가 재시작되면 연결된 모든 클라이언트가 동시에 재연결하면서 전체 장애로 이어질 수 있는 재연결 폭풍을 만듭니다. Solana에서는 표준 WebSocket 구현이 "부하 상태에서 신뢰할 수 없게" 되어 성능이 저하되고 자주 연결이 끊겼고, 이는 Solana 팀들이 gRPC로 옮겨가는 계기가 되었습니다.

gRPC는 브라우저 엣지에서 어려움을 겪습니다. 브라우저는 gRPC를 네이티브로 말할 수 없습니다. [gRPC-Web protocol](https://github.com/grpc/grpc-web)이 프록시(보통 Envoy)를 통해 그 간극을 메워주지만, 그 과정에서 클라이언트 스트리밍과 양방향 스트리밍을 잃습니다. 바이너리 protobuf 페이로드는 curl이나 브라우저 DevTools로 확인할 수 없어, 개발자 경험이 중요한 공개용 API에는 gRPC가 잘 맞지 않습니다. protobuf 툴체인(스키마 정의, 코드 생성, 빌드 통합)은 단순한 통합에도 의미 있는 설정 비용을 추가합니다.

## Webhooks, WebSockets, gRPC 중 어떻게 선택해야 하는가?

여러분의 사용 사례가 필요로 하는 통신 패턴에서 시작하세요.

Webhooks가 기본값입니다. 백엔드가 개별 이벤트가 발생했을 때(트랜잭션 확정, NFT 전송, 결제 완료) 이를 알아야 하고 이벤트 발생률이 초당 수백 건 이내라면, webhooks는 어떤 언어, 어떤 프레임워크, 어떤 방화벽에서도 동작하는 가장 저비용의 통합 방법입니다. 지속적인 연결도, 스트리밍 인프라도, 클라이언트 SDK도 필요 없습니다.

이벤트 발생률이 올라가거나 데이터가 양방향으로 흘러야 할 때는 WebSockets가 그 역할을 맡습니다. 실시간 시세 표시기, 오더북 업데이트, 멤풀 모니터링, 협업 대시보드처럼 브라우저에 대한 지속적인 연결이 API를 계속 두드리는 것을 막아주는 모든 것을 생각해보세요. 네이티브 브라우저 지원, 프록시 불필요, 바이트 단위로 측정되는 프레임 오버헤드입니다.

백엔드 서비스 간 최대 처리량이 필요할 때 gRPC를 선택하세요. 인덱서 파이프라인, 밸리데이터 데이터 스트리밍, 마이크로서비스 간 통신, 그리고 연결의 양쪽 끝을 모두 제어하는 모든 워크로드는 바이너리 직렬화, 멀티플렉싱된 스트림, 타입이 지정된 계약, 내장된 백프레셔의 혜택을 받습니다. 함정은 툴체인입니다: protobuf 스키마, 코드 생성, 네이티브 브라우저 지원 없음. 성능과 신뢰성이 설정 복잡도보다 중요할 때 그만한 가치가 있습니다.

아키텍처가 요구할 때는 이들을 조합하세요. 많은 프로덕션 시스템이 세 가지 모두를 사용합니다: gRPC가 밸리데이터에서 인덱서로 데이터를 스트리밍하고, WebSockets가 처리된 데이터를 브라우저 대시보드로 푸시하고, webhooks가 개별 이벤트를 외부 시스템에 알립니다. 단일 프로토콜에 모든 것을 걸어야 할 이유는 없습니다.

<EmbeddedTable
  table={{
    columns: [
      { key: "useCase", width: 280, title: "Use case", dataType: "object" },
      { key: "protocol", width: 180, title: "Recommended protocol", dataType: "object" },
      { key: "why", width: 320, title: "Why", dataType: "object" },
    ],
    data: [
      {
        useCase: { title: "Transaction confirmation alerts", tooltip: "", icon: "" },
        protocol: { title: "Webhooks", tooltip: "", icon: "" },
        why: { title: "Discrete events, simple integration, at-least-once delivery", tooltip: "", icon: "" },
        id: 0,
      },
      {
        useCase: { title: "Live price feed in a trading UI", tooltip: "", icon: "" },
        protocol: { title: "WebSockets", tooltip: "", icon: "" },
        why: { title: "Continuous data to browser, low latency, bidirectional", tooltip: "", icon: "" },
        id: 1,
      },
      {
        useCase: { title: "Solana validator data pipeline", tooltip: "", icon: "" },
        protocol: { title: "gRPC", tooltip: "", icon: "" },
        why: { title: "Maximum throughput, binary serialization, backpressure", tooltip: "", icon: "" },
        id: 2,
      },
      {
        useCase: { title: "Ethereum new block subscriptions", tooltip: "", icon: "" },
        protocol: { title: "WebSockets", tooltip: "", icon: "" },
        why: { title: "Standard eth_subscribe interface, browser-compatible", tooltip: "", icon: "" },
        id: 3,
      },
      {
        useCase: { title: "Backend microservice communication", tooltip: "", icon: "" },
        protocol: { title: "gRPC", tooltip: "", icon: "" },
        why: { title: "Typed contracts, multiplexing, deadline propagation", tooltip: "", icon: "" },
        id: 4,
      },
      {
        useCase: { title: "Third-party integration callbacks", tooltip: "", icon: "" },
        protocol: { title: "Webhooks", tooltip: "", icon: "" },
        why: { title: "Universal HTTP compatibility, no persistent connection", tooltip: "", icon: "" },
        id: 5,
      },
    ],
  }}
/>

## Alchemy로 실시간 블록체인 데이터를 구축하세요

저희는 [100개 이상의 체인](https://www.alchemy.com/rpc)에서 세 가지 프로토콜을 모두 지원합니다.

저희 [Custom Webhooks](https://www.alchemy.com/webhooks)는 트랜잭션 확정, 주소 활동, NFT 이벤트를 HMAC 서명 검증과 자동 재시도를 갖춘 HTTP POST 콜백으로 전달합니다. GraphQL 필터를 사용하면 애플리케이션에 필요한 정확한 컨트랙트 이벤트만 구독할 수 있습니다.

[Smart WebSockets](https://www.alchemy.com/smart-websockets)는 필터링된 eth_subscribe 스트림을 자동 재연결과 함께 제공하며, 실시간 프론트엔드 애플리케이션의 지연 시간을 줄여줍니다.

대용량 데이터를 처리하는 Solana 팀을 위해, 저희 [Yellowstone gRPC streaming](https://www.alchemy.com/docs/reference/yellowstone-grpc-overview)은 밸리데이터 메모리에서 직접 계정 업데이트와 트랜잭션을 10ms 미만의 지연 시간으로 전달합니다.

세 가지 모두 무료 티어에서 사용할 수 있습니다. 계약도, 대기자 명단도 없습니다. [가입](https://dashboard.alchemy.com/signup)하고 몇 분 안에 스트리밍을 시작하세요.

## 자주 묻는 질문

### Webhooks, WebSockets, gRPC의 주요 차이점은 무엇인가요?

Webhooks는 서버 간 이벤트 알림을 위한 단방향 HTTP 콜백이고, WebSockets는 클라이언트와 서버 간 지속적이고 양방향인 연결을 제공하며, gRPC는 타입이 지정된 계약을 통한 높은 처리량의 서비스 간 통신을 위해 설계된 HTTP/2 기반 RPC 프레임워크입니다.

### WebSockets나 gRPC 대신 webhooks를 언제 사용해야 하나요?

트랜잭션 확정이나 NFT 전송 같은 개별 이벤트 알림이 초당 수백 건 이내의 빈도로 필요하고 지속적인 연결이 필요하지 않을 때 webhooks를 사용하세요. 특별한 클라이언트 SDK나 인프라가 필요 없는 가장 단순한 통합 방법입니다.

### WebSockets가 더 나은 선택인 경우는 언제인가요?

WebSockets는 실시간 시세 표시기, 오더북 업데이트, 대시보드처럼 브라우저 클라이언트로의 지속적이고 실시간인 데이터 스트리밍에 가장 적합합니다. 초기 핸드셰이크 이후 네이티브 브라우저 지원과 낮은 메시지당 오버헤드(2-6바이트)를 제공합니다.

### gRPC는 어떤 문제를 해결하도록 설계되었나요?

gRPC는 인덱서 파이프라인이나 밸리데이터 데이터 스트리밍처럼 구조화된 데이터를 높은 속도로 이동시키는 백엔드 서비스에 최적화되어 있습니다. 바이너리 직렬화, 멀티플렉싱된 스트림, Protocol Buffers를 통한 타입이 지정된 계약, 내장된 백프레셔, 데드라인 전파를 제공합니다.

### Webhooks, WebSockets, gRPC를 같은 시스템에서 함께 사용할 수 있나요?

네, 많은 프로덕션 시스템이 세 가지를 모두 조합합니다: 백엔드 서비스 간 통신에는 gRPC, 실시간 브라우저 업데이트에는 WebSockets, 개별 이벤트를 외부 시스템에 알리는 데는 webhooks를 사용합니다. 각 프로토콜은 서로 다른 아키텍처 계층을 해결합니다.

### 이 프로토콜들 사이의 성능 차이는 무엇인가요?

gRPC는 바이너리 인코딩과 HTTP/2 멀티플렉싱을 통해 가장 낮은 지연 시간과 가장 높은 처리량을 제공하고, WebSockets는 브라우저 스트리밍에 대해 낮은 오버헤드(메시지당 2-6바이트)를 제공하며, webhooks는 전체 HTTP 왕복 오버헤드로 인해 메시지당 비용이 가장 높지만 성능보다 단순함을 우선시합니다.

### 각 프로토콜의 주요 단점은 무엇인가요?

Webhooks는 고빈도 이벤트와 버스트 트래픽에서 어려움을 겪고, WebSockets는 연결 교체와 thundering-herd 재연결 폭풍으로 인한 확장성 문제에 직면하며, gRPC는 protobuf 툴체인 설정이 필요하고 네이티브 브라우저 지원이 없어 웹 클라이언트를 위한 프록시(gRPC-Web)가 필요합니다.

### Alchemy는 블록체인 애플리케이션을 위해 이 프로토콜들을 어떻게 지원하나요?

Alchemy는 100개 이상의 체인에서 이벤트 알림을 위한 Custom Webhooks, 자동 재연결 기능을 갖춘 필터링된 eth_subscribe 스트림을 위한 Smart WebSockets, 그리고 밸리데이터 메모리에서 직접 10ms 미만의 지연 시간으로 계정 업데이트를 전달하는 Solana의 Yellowstone gRPC streaming을 제공합니다.
