Supabase Realtime binary payload는 “JSON 최적화”가 아니라 클라이언트 호환성 계약이다
실시간 기능에서 payload를 binary로 바꾸면 서버 비용만 줄고 끝일까?
Supabase Realtime binary payload는 “JSON 최적화”가 아니라 클라이언트 호환성 계약이다
- 카테고리: web_app_dev
- 예상 읽기 시간: 10분
- 오늘의 질문: 실시간 기능에서 payload를 binary로 바꾸면 서버 비용만 줄고 끝일까?
- 핵심 출처:
- Supabase Changelog: Realtime Broadcast now supports binary payloads - 게시일: 2026-07, 확인일: 2026-07-26
- Supabase Docs: Realtime Broadcast - 문서, 확인일: 2026-07-26
- Supabase Docs: Realtime Protocol - 문서, 확인일: 2026-07-26
- supabase/supabase Developer Update - July 2026 - 게시일: 2026-07-09, 확인일: 2026-07-26
1. 왜 지금 봐야 하나
Supabase의 2026년 7월 Developer Update에는 Realtime Broadcast가 JSON뿐 아니라 binary payload도 주고받을 수 있다는 소식이 들어갔다. Changelog는 sensor telemetry, GPS 좌표, accelerometer 값, live screenshot streaming처럼 숫자가 많거나 이미 binary인 데이터를 JSON text로 감싸지 않고 보낼 수 있다고 설명한다. REST API, client library의 WebSocket, database 함수 realtime.send_binary()까지 세 경로에서 binary broadcast를 지원한다는 점도 중요하다.
하지만 이 기능을 “더 빠른 실시간 메시지” 정도로만 보면 위험하다. 공식 문서는 최소 버전을 분명히 적는다. WebSocket 수신은 supabase-js 2.91.0 이상, supabase-swift 2.44.0 이상에서 자동 처리된다. REST httpSend()의 binary payload는 supabase-js 2.107.0 이상이 필요하다. Realtime server는 2.103.2 이상이 필요하다. 그리고 가장 운영적으로 중요한 문장: 지원하지 않는 구버전 SDK 또는 아직 지원하지 않는 Dart, Kotlin, Python client로 보낸 binary 메시지는 조용히 drop될 수 있다.
웹앱 개발자에게 이건 단순 성능 옵션이 아니다. payload encoding을 바꾸는 순간 서버, 브라우저, 모바일 앱, SDK 버전, REST endpoint, DB 함수, fallback format이 동시에 맞아야 한다. 오늘의 핵심 문장: 실시간 binary payload는 “데이터를 작게 보내는 기술”이 아니라 “누가 어떤 형식을 이해하는지 명시하는 호환성 계약”이다.
2. 핵심 개념
Realtime Broadcast는 보통 “topic에 event를 뿌린다”로 이해한다. JSON만 쓸 때는 payload가 객체인지 문자열인지 정도만 신경 쓰면 된다.
await channel.send({
type: 'broadcast',
event: 'cursor-pos',
payload: { x: 120, y: 240 },
})
binary를 쓰면 질문이 바뀐다.
await channel.send({
type: 'broadcast',
event: 'cursor-pos',
payload: new Uint8Array([120, 0, 240, 0]).buffer,
})
이제 receiver는 { x, y } 객체를 받는 것이 아니라 byte 배열을 해석해야 한다. 첫 2바이트가 x인지, little-endian인지, version byte가 있는지, 좌표 단위가 pixel인지 normalized value인지 알아야 한다. 즉 payload format은 API 스키마가 된다.
Supabase Realtime Protocol 문서를 보면 이 변화가 더 분명하다. protocol v2.0.0은 text frame뿐 아니라 binary WebSocket frame을 지원한다. binary broadcast frame에는 payload encoding byte가 있으며 0 = binary, 1 = JSON으로 구분된다. USER_BROADCAST_PUSH, USER_BROADCAST 같은 타입 코드와 topic, event, metadata, user payload가 정해진 순서로 들어간다.
여기서 개발자가 가져갈 개념은 “binary는 JSON의 압축판이 아니다”이다. JSON은 self-describing에 가깝다. key 이름이 있고, 사람이 로그에서 읽을 수 있고, schema drift를 어느 정도 눈으로 발견할 수 있다. binary는 작고 빠를 수 있지만, 해석 규칙을 코드 밖에서 합의해야 한다. 그래서 binary payload를 도입할 때는 다음 세 가지를 같이 설계해야 한다.
- format version: payload 첫 byte나 metadata에 버전을 둔다.
- decoder ownership: web, iOS, backend worker가 같은 decoder 규칙을 공유한다.
- fallback path: binary를 못 받는 client가 JSON event나 capability check로 빠질 길을 둔다.
3. 최신 이슈와 연결
공식 Changelog는 binary payload가 모든 전송 경로에서 가능하다고 말한다.
- Client libraries over WebSocket:
ArrayBuffer또는ArrayBufferView를 payload로 전달 - REST API: single-message endpoint에서
Content-Type이application/json인지application/octet-stream인지로 payload type 결정 - Database:
realtime.send_binary()로byteapayload 전송
Broadcast 문서는 REST 예시도 바꿨다. 단일 메시지는 topic과 event가 path에 들어간다.
curl -v \
-H 'apikey: <SUPABASE_TOKEN>' \
-H 'Content-Type: application/octet-stream' \
--data-binary @payload.bin \
'https://<PROJECT_REF>.supabase.co/realtime/v1/api/broadcast/test/events/event?private=true'
반면 batch endpoint POST /realtime/v1/api/broadcast는 JSON body의 messages 배열을 받으며 JSON payload 전용으로 남아 있다. 이 차이는 실무에서 중요하다. 운영자가 “batch로 여러 binary frame을 보내자”고 생각하면 공식 경로와 어긋난다. binary는 single-message endpoint, WebSocket send, database send_binary()처럼 지원되는 경로별 제약을 확인해야 한다.
또 하나의 연결점은 private channel이다. 문서는 database broadcast의 is_private flag가 누가 subscribe할 수 있는지를 제어한다고 설명한다. binary broadcast도 public/private matching rule을 따른다. binary를 쓰더라도 auth, topic naming, RLS 기반 private channel 검증은 사라지지 않는다. payload가 byte가 되면 오히려 로그와 디버깅이 어려워지므로 권한 경계는 더 명시적이어야 한다.
4. 개발자 관점 해석
실시간 웹앱에서 binary payload가 매력적인 순간은 분명하다.
- 협업 캔버스에서 cursor, brush stroke, viewport matrix를 초당 여러 번 보낼 때
- IoT dashboard에서 수천 개 기기의 numeric reading을 계속 받을 때
- 고객 지원 도구에서 작은 screenshot frame이나 binary diff를 broadcast할 때
- 게임/멀티플레이 UI에서 위치, 입력, tick 정보를 촘촘히 보낼 때
하지만 대부분의 제품은 “모든 것을 binary로 바꾸자”가 답이 아니다. JSON으로 충분한 이벤트는 JSON으로 남기는 편이 낫다. 예를 들어 user_joined, toast, notification, document_renamed 같은 이벤트는 빈도도 낮고 디버깅 가능성이 중요하다. 반대로 high-frequency numeric stream은 binary 후보가 된다.
실무 기준은 이렇게 잡을 수 있다.
| 질문 | JSON 유지 | binary 검토 |
|---|---|---|
| 사람이 로그에서 바로 읽어야 하나? | 예 | 아니오 |
| payload가 초당 많이 반복되나? | 아니오 | 예 |
| 숫자/좌표/프레임처럼 고정 폭 구조인가? | 아니오 | 예 |
| 모든 client SDK가 최소 버전을 만족하나? | 불확실 | 확인됨 |
| 구버전 client drop을 감당할 수 있나? | 아니오 | fallback 있음 |
AI 코딩 도구를 쓰는 팀이라면 더 조심해야 한다. “Supabase Realtime binary payload로 최적화해줘”라고 지시하면 에이전트는 Uint8Array 예시만 만들고 decoder, versioning, SDK minimum, fallback, observability를 빠뜨릴 수 있다. 좋은 지시는 다음처럼 더 구체적이어야 한다.
cursor-pos event만 binary v1으로 바꾸되, payload 첫 byte는 version, 다음 2바이트는 little-endian x, 다음 2바이트는 little-endian y로 한다. receiver가 binary를 지원하지 않거나 decoder error가 나면 cursor-pos-json event를 사용한다. web client는 supabase-js >= 2.91.0을 package.json과 runtime check에 반영한다.
5. 내 프로젝트에 적용할 체크포인트
-
이벤트별 payload inventory를 만든다
cursor-pos,typing,presence,chat-message,screen-frame처럼 event를 나눈다.- 빈도, 평균 크기, 사람이 읽어야 하는지, 유실 가능 여부를 표시한다.
-
binary 후보는 format spec을 먼저 쓴다
- byte order, field width, version, optional field 처리 방식을 문서화한다.
- TypeScript decoder와 encoder를 같은 테스트 fixture로 검증한다.
-
SDK 최소 버전을 배포 조건으로 건다
- web:
@supabase/supabase-js >= 2.91.0for WebSocket receive - REST
httpSend()binary:@supabase/supabase-js >= 2.107.0 - iOS:
supabase-swift >= 2.44.0 - Dart/Kotlin/Python client가 끼어 있으면 binary event를 필수 기능으로 만들지 않는다.
- web:
-
silent drop을 장애로 관측한다
- sender ack만 보고 성공으로 판단하지 않는다.
- receiver가 일정 시간 안에 “binary-capable” heartbeat나 app-level ack를 보내는지 본다.
- 구버전 client 비율을 feature flag rollout 지표에 넣는다.
-
권한 경계를 JSON 때와 동일하게 둔다
- private channel 여부, topic naming, auth token refresh, RLS 정책을 그대로 점검한다.
- binary payload 안에 민감 정보를 숨겼다고 생각하지 않는다. binary는 암호화가 아니다.
6. 오늘 10분 액션
오늘은 코드를 크게 바꾸지 말고 “binary 도입 가능성 표”만 만들어보자.
| event | 현재 format | 초당 빈도 | 평균 크기 | 유실 허용 | binary 후보 | fallback |
| --- | --- | ---: | ---: | --- | --- | --- |
| cursor-pos | JSON {x,y} | 20/user | 18~40B+overhead | 예 | 예 | cursor-pos-json |
| chat-message | JSON object | 낮음 | 다양 | 아니오 | 아니오 | 유지 |
| screen-frame | base64 JSON | 5/viewer | 큼 | 예 | 예 | JPEG URL 또는 낮은 FPS |
그다음 package.json에서 @supabase/supabase-js 버전을 확인하고, 모바일/서버 worker가 같은 event를 구독하는지 적어라. binary 전환은 성능 작업처럼 보이지만 실제로는 release coordination 작업이다.
7. 더 볼 자료
- Supabase Realtime Broadcast guide: WebSocket, REST, database broadcast와 binary payload 사용법
- Supabase Realtime Protocol: protocol v2.0.0 text/binary frame 구조와 payload encoding byte
- Realtime Broadcast binary payload changelog: 최소 SDK/server 버전과 silent drop 주의점
- Supabase Developer Update - July 2026: July 2026 Supabase 변경 요약
중복 회피 메모
로컬 content/generated와 Supabase 최근 web_app_dev 글을 확인했다. 최근 글은 Hono Object.create(null) 입력 객체 경계, Next.js Partial Prefetching/App Shell, Supabase Pipelines/CDC 분석 분리, Next.js 월간 보안 릴리스, SWC WASM/lockfile, React Router Web Streams, TypeScript 7, pg_graphql introspection, PostgREST JWT kid, Supabase Auth URL 라우팅을 다뤘다. 2026-06-13 글에서 Supabase Realtime과 ETL을 비교한 적은 있지만, 이번 글은 Realtime Broadcast의 binary payload 지원을 payload encoding, SDK 최소 버전, silent drop, REST/database/WebSocket 전송 경로 차이, fallback 설계라는 클라이언트 호환성 계약 관점으로 다룬다.
핵심 출처
로그인하면 이 글을 북마크하고, 나만 보는 한 줄 메모를 남길 수 있어요.
댓글 0
최신순 ▾혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)