<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

[PonsLink] WebRTC 시그널링과 TURN 인프라 구축 회고

Declan Jeon·2026년 8월 7일

  • 개발 회고

실시간 제품을 만들면 처음엔 영상이 붙는 순간에 환호한다. 그다음 주부터는 NAT 뒤의 사용자, 회사 방화벽, 모바일 전환, 재연결 폭풍을 상대한다. PonsLink의 시그널링·TURN·SFU 구성은 그 실패를 제품 상태로 다루기 위한 결과다. 시그널링 경로 클라이언트는 nginx의 `/socket.io/` 로케이션을 통해 API 컨테이너에 WebSocket 업그레이드로 붙는다. nginx는 `Upgrade`/`Connection` 헤더를 넘기고, `proxy_read_timeout`을 3600초로 둔다. 버퍼링은 끈다. API 쪽 모듈: &#8211; &#8230; Read more

실시간 제품을 만들면 처음엔 영상이 붙는 순간에 환호한다. 그다음 주부터는 NAT 뒤의 사용자, 회사 방화벽, 모바일 전환, 재연결 폭풍을 상대한다. PonsLink의 시그널링·TURN·SFU 구성은 그 실패를 제품 상태로 다루기 위한 결과다.

시그널링 시퀀스

시그널링 경로

클라이언트는 nginx의 `/socket.io/` 로케이션을 통해 API 컨테이너에 WebSocket 업그레이드로 붙는다. nginx는 `Upgrade`/`Connection` 헤더를 넘기고, `proxy_read_timeout`을 3600초로 둔다. 버퍼링은 끈다.

API 쪽 모듈:

– `signaling/broker.ts` — 룸/세션 이벤트 라우팅

– `signaling/redisAdapter.ts` — 인스턴스 간 pub/sub

– `signaling/replayStore.ts` — 재연결 시 누락 이벤트 재생

– `signaling/waitingAlert.ts` — 호스트 수락 전 대기 상태 전파

Redis가 빠지면 수평 확장이 아니라 상태 단절이 된다. 어댑터를 시그널링의 일부로 취급해야 한다.

TURN 자격증명

`turn/turnCredentials.ts`가 HMAC 기반 단기 자격증명을 발급한다. 실측 env:

– `TURN_SERVER_URLS=turn:turn.ponslink.com:3478?transport=udp, …tcp, turn:…:5349?transport=tcp`

– `TURN_CREDENTIAL_TTL_SECONDS=600`

– `TURN_REALM=ponslink.com`

장기 공유 비밀번호를 클라에 심지 않는다. 600초면 세션 시작에는 충분하고, 유출 시 피해 창도 짧다. coturn은 docker 컨테이너(`docker-coturn-1`)로 돌린다.

SFU와 P2P의 경계

기본 미디어 런타임은 `MEDIA_RUNTIME_PROVIDER=pion-p2p`다. 1:1·소규모는 메시 P2P가 싸고 단순하다. 다자간·대역 비대칭이 커지면 SFU(`wss://ponslink.com/sfu/ws`, RTC 40000–40100, announced IP)로 올린다. `P2P_FORCE_TURN=false` — TURN은 폴백이지 기본 경로가 아니다. TURN 중계 비용과 지연을 기본값으로 지불하고 싶지 않았다.

룸 안 도구들도 같은 파이프를 탄다

화이트보드 이벤트, 폰캐스트 플레이리스트, 파일 전송 시그널, 자막 동기화는 각자 REST + 실시간 채널을 섞어 쓴다. 공통 규칙은 하나다.

1. **권한 확인은 API**에서 한다 (session-tokens, session-access).

2. **바이트 이동은 가능한 한 피어/오브젝트 스토리지**로 보낸다.

3. **상태 복구는 replay**로 가능하게 남긴다.

폰캐스트는 로컬 파일을 클라우드에 올리지 않고 미리보기·공유하는 면이다, 수신 오류는 사용자가 복구할 수 있는 메시지로 끝나야 한다. "영원히 로딩"은 버그가 아니라 설계 실패다.

운영 지표

TURN 면에는 `/turn/health`, `/turn/metrics`, `/turn/analytics/usage`, `/turn/security/events`가 있다. 시그널링 성공률, ICE 상태, TURN 사용 비율을 제품 지표로 보지 않으면 장애를 "네트워크 탓"으로만 넘기게 된다.

실시간 인프라는 마법이 아니다. 짧은 자격증명, 재연결 재생, 폴백 경로, 관측 가능한 상태 네 가지를 빼먹으면 데모 다음 주에 무너진다.

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 L1] PonsLink P2P Room — WebRTC 화상회의를 BFF로 감싼 이유

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

댓글

아직 댓글이 없습니다.

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