<PonsLink/>
작업
글
주제
시리즈
카테고리
연락
⌘K 검색
About
코드와 제품으로

코드와 제품으로

연결되는 세계

연결되는 세계

PonsLink는 만남 뒤 요청을 세션으로 잇고, PonsWarp는 서버 보관 없이 파일을 직접 전송합니다. 이 블로그는 기능 소개보다 설계 판단과 운영 증거를 남깁니다.

연락처

[email protected]github.com/DeclanJeon

운영

콘텐츠는 WordPress headless CMS, 공개 UI는 Next.js입니다.

초안 미리보기
© 2026 PonsLink Blog|Next.js · WordPress REST · Framer Motion
Local-first · Backup-before-deploy

[Blueprint L1] PonsLink P2P Room — WebRTC 화상회의를 BFF로 감싼 이유

Declan·2026년 8월 28일

  • Blueprint
  • P2P/분산기술
  • 개발 회고

React SPA와 Express BFF가 Socket.IO로 시그널링하고, 미디어는 mesh로 가는 2티어. TURN 그랜트와 presence lease로 룸을 만든 화상회의.

# [Blueprint L1] PonsLink P2P Room — WebRTC 화상회의를 BFF로 감싼 이유 > Series: `blueprint-ponslink` (1/3) · Category: `P2P/분산기술` + `개발 회고` · Slug: `blueprint-ponslink-06-p2p-webrtc-room` > Source: `blueprints/specs/P02_ponslink_web.md` §06 + `blueprints/06-pons-p2p.png` + `pons_p2p/ponslink-room-frontend + ponslink-api-infra` — ## 0. TL;DR `pons_p2p`는 `React 18 SPA`와 `Express BFF`가 `Socket.IO`로 시그널링을 주고받고, 미디어는 브라우저끼리 `WebRTC mesh`로 간다. `BFF`가 `TURN 자격증명`과 `R2 presigned URL`을 발급하고, `Postgres(hydration 게이트)`와 `Redis(presence lease)`가 룸 상태를 들고 있다. 서버는 릴레이가 아니라 `그랜트`를 주는 곳이다.
PonsLink P2P Room 설계도 06
— ## 1. 문제 정의 — 왜 룸에 BFF가 필요한가 화상회의에서 서버가 할 일은 두 가지: “누가 들어올 수 있는가”와 “연결을 어떻게 돕는가”. `PonsLink`는 `desks`와 `rooms`를 오가는 서비스라, 룸마다 `기능 그랜트`가 다르다. `프론트`가 직접 `TURN` 서버에 계정을 만들면 남용된다. 그래서 `BFF(api-infra)`가 `그랜트`를 발급하고, 시그널링은 `Socket.IO`로, 미디어는 `mesh`로 위임했다. — ## 2. 설계도 한눈에 — 2티어 왼쪽 `React SPA(Zustand)` ↔ 중앙 `Express BFF + Socket.IO(Node20)` ↔ 오른쪽 두 브라우저 사이 `WebRTC mesh` 굵은 화살표. `coturn TURN`은 점선으로만 경유. `BFF` 아래 `Postgres(hydration 게이트)`와 `Redis(presence lease TTL)`와 `R2`가 있다. 전체를 `Docker Compose(coturn)`이 감싸고, `BFF 컨트롤 / 메시 데이터`로 2티어를 구분. * **시그널링:** `offer/answer/ICE relay`는 `Socket.IO`로만. `BFF`가 `그랜트`를 확인한 뒤에만 `relay`. * **미디어:** `BFF`를 안 거치고 브라우저끼리 직접. `TURN`은 NAT가 막혔을 때만. * **데이터:** `Postgres`는 `hydration 게이트` — DB 상태 확인 후 UI 복원. `Redis`는 `presence lease` — TTL로 “누가 지금 방에 있는가”를 들고, 끊기면 자동 만료. — ## 3. 아키텍처 해부 — BFF가 하는 3가지 “` room-frontend(React) –Socket.IO–> api-infra(Express) ├── grant check ├── TURN cred 발급 └── R2 presigned URL –WebRTC mesh–> peer “` * **grant check:** `roomId`와 `userId`로 `Postgres`를 조회해 “입장 가능”을 확인. `hydration 게이트`를 통과해야 `Socket.IO`에 `join`. * **TURN cred:** `coturn`용 `username:expiry:hmac`를 1시간 TTL로 발급. `BFF`가 아니면 발급 불가. * **R2 presigned:** 파일 업로드는 `BFF`가 `R2 presigned PUT`을 발급하고, 브라우저가 `R2`에 직접 `PUT`. 서버는 바이트를 안 본다. — ## 4. 로직 딥다이브 — 2개만 깊게 ### 4.1 presence lease (Redis TTL) * `join` 시 `SET presence:{roomId}:{userId} 1 EX 30` — 30초 lease. 클라이언트가 20초마다 `heartbeat`로 갱신. 끊기면 30초 뒤 자동 삭제. `replay`는 최근 `presence`를 `Redis`에서 읽어 복원. ### 4.2 hydration 게이트 (Postgres) * `room-frontend`가 `Socket.IO`에 연결해도 바로 방을 그리지 않는다. `BFF`가 `Postgres`에서 `room`과 `grants`를 읽어 `hydration` 데이터를 내려줘야 `Zustand`가 `UI`를 복원. 새로고침해도 방 상태가 유지되는 이유. — ## 5. 왜 이 선택인가 | 선택 | 채택 | 버린 대안 | 버린 이유 | |—|—|—|—| | 시그널링 | Socket.IO + BFF 그랜트 | 프론트 직접 WebSocket | 그랜트 없이 누구나 `offer`를 쏠 수 있음 | | 미디어 | mesh 직접 | SFU 서버 릴레이 | 비용, E2E 암호화 불가 | | presence | Redis lease | DB 폴링 | TTL 자동 만료, `replay`가 간단 | — ## 6. 실패와 복구 * **실패:** 처음엔 `presence`를 `Postgres`에 두었다. 그러나 `heartbeat`가 20초마다 `UPDATE`를 쏘자 `DB`가 버티지 못했다. `Redis`로 옮기자 `QPS` 100배 감소. * **복구:** `TURN` 자격증명을 24시간으로 길게 줬다가 남용. 1시간으로 줄이고, `BFF`가 `그랜트` 없으면 발급 거부. — ## 7. 운영 증거 * `ponslink-room-frontend` — `React 18, Vite 5, Zustand, socket.io-client` * `ponslink-api-infra` — `Node 20, Express, Socket.IO, Postgres, Redis, R2, coturn, Docker Compose` * `hydration 게이트` — `Postgres` 상태 확인 후 `UI` 복원, `presence lease` 30초 — ## 8. 다음 편 예고 **L2: `control-tower` — Threads SNS 자동발행.** `cardnews` 재사용→생성 폴백, `n8n` 워크플로 패치, `dry-run` 기본 게이트를 다룬다. *부록: `blueprints/06-pons-p2p.png` 프롬프트 `prompts.part1.json#06`, 코드 진입점 `pons_p2p/ponslink-api-infra/src/socket/roomHandler.ts`*

Related

관련 운영 노트

같은 주제에서 이어서 읽기 좋은 글입니다.

  • [Blueprint L3] Pons 포트폴리오 — 의존성 0, 단일 index.html이 오래 가는 이유

    의존성 0, 단일 index.html 41KB. 디자인 토큰 SSOT와 13개 제품 쇼케이스로 만든 정적 포트폴리오가 오래 가는 이유.

  • [Blueprint L2] PonsLink Control Tower — Threads에 dry-run으로 쏘는 이유

    Python Threads 컨트롤타워. cardnews 재사용→생성 폴백, n8n 패치, dry-run 기본 게이트로 안전하게 쏘는 자동발행.

  • [Blueprint W5] PonsWarp Desktop — WarpInfra, 브라우저를 떠난 QUIC 메시

    브라우저를 떠난 QUIC 메시. Tauri2 warp-core, warp-agentd, mDNS, rarest-first로 인트라넷 릴리스 그리드를 만든 인프라.

댓글

아직 댓글이 없습니다.

현재 이 글의 댓글은 닫혀 있습니다.