<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] desk.ponslink.com은 어떻게 돌아가는가 — 배포 토폴로지 회고

Declan Jeon·2026년 8월 7일

  • 개발 회고

PonsLink를 운영하면서 가장 자주 받는 질문은 단순하다. &quot;이 서비스, 실제로 어떻게 붙어 있어요?&quot; 마케팅 문구로는 &quot;요청-우선 세션 데스크&quot;라고 말하지만, 그 문장 뒤에는 nginx 한 줄, Node 컨테이너 한 개, Postgres 두 개의 DB, Redis, coturn, SFU, R2, 결제 공급자 네 곳이 동시에 움직인다. 이 글은 2026-08-07 기준 `ssh ponslink`에서 실측한 desk.ponslink.com의 배포 구조를 정리한다. 한 &#8230; Read more

PonsLink를 운영하면서 가장 자주 받는 질문은 단순하다. "이 서비스, 실제로 어떻게 붙어 있어요?" 마케팅 문구로는 "요청-우선 세션 데스크"라고 말하지만, 그 문장 뒤에는 nginx 한 줄, Node 컨테이너 한 개, Postgres 두 개의 DB, Redis, coturn, SFU, R2, 결제 공급자 네 곳이 동시에 움직인다. 이 글은 2026-08-07 기준 `ssh ponslink`에서 실측한 desk.ponslink.com의 배포 구조를 정리한다.

서비스 아키텍처 총론

한 줄로 보면

브라우저 → nginx(TLS 443) → (정적 SPA | `/api`·`/auth` | `/socket.io`) → ponslink-api(Node 24, :6650) → Postgres / Redis / coturn / SFU / R2 / 외부 API.

프론트엔드는 React SPA다. nginx는 `/home/declan/ponslink-frontend-dist`를 루트로 두고, 앱 라우트(`/public-desk`, `/room`, `/lounge`, `/login` 등)는 `app-shell.html`로 떨어뜨린다. `/assets/`는 1년 캐시, `sw.js`는 no-cache다. 비즈니스 트래픽은 전부 같은 호스트의 upstream `127.0.0.1:6650`으로 프록시된다.

API 컨테이너가 들고 있는 것

실측 env 기준 API는 다음을 동시에 붙잡는다.

– **DB**: `DATABASE_URL=…/ponslink` (세션·미디어·웹훅 도메인). 같은 Postgres 인스턴스에 `ponswarp` DB(계정·결제·클라우드 공유)가 나란히 있다.

– **Redis**: Socket.IO 어댑터 + 레이트리밋.

– **TURN**: `turn.ponslink.com:3478` udp/tcp, `:5349` tcp. HMAC 자격증명 TTL 600초.

– **SFU**: `wss://ponslink.com/sfu/ws`, RTC 포트 40000–40100.

– **스토리지**: Cloudflare R2 presigned URL, 객체 TTL 180분, 정리 스케줄러 가동.

– **결제**: `PAYMENT_PROVIDER=polar`. PayPal sandbox, LemonSqueezy/Stripe 웹훅 호환 유지.

– **미디어/AI**: Azure Translator·Speech, Deepgram, Google OAuth/Calendar/Translate, Giphy, OpenRouter.

모듈 경계는 소스 기준으로 `auth / billing / signaling / turn / providers / store / webhooks / security`로 나뉜다. `routes.ts`에는 260개 넘는 REST 라우트가 등록돼 있고, 실시간은 Socket.IO 브로커(`signaling/broker.ts` + `redisAdapter.ts` + `replayStore.ts`)가 맡는다.

왜 이렇게 쪼갰는가

초기에 "방만 만들면 된다"고 생각했다. 틀렸다. 요청 수집, 결제 게이트, 세션 미디어, 기록 보관은 실패 모드가 다르다. 그래서:

1. **엣지(nginx)** 는 TLS·정적·WebSocket upgrade만 담당한다.

2. **API** 는 비즈니스 상태와 시그널링을 한 프로세스에서 처리하되, 미디어 중계는 SFU/TURN으로 넘긴다.

3. **파일 바이트** 는 API를 경유하지 않는다. R2 presigned URL로 클라이언트가 직접 PUT/GET 한다.

4. **결제 상태** 는 공급자 웹훅 + 이벤트 원장 + 멱등성 키로만 확정한다.

이 분리는 "데모는 되는데 운영이 흔들리는" 지점을 줄이기 위한 선택이었다. 다음 글에서는 그 상태를 담는 데이터 모델(ERD)을 풀어쓴다.

실측 체크리스트

– `cat /etc/nginx/sites-enabled/desk.ponslink.com`

– `docker inspect docker-ponslink-api-1`

– `docker exec docker-postgres-1 psql -U ponswarp -d ponslink -c '\dt'`

– `docker exec docker-postgres-1 psql -U ponswarp -d ponswarp -c '\dt'`

– `systemctl status` / `docker ps` — nginx, docker-ponslink-api-1, postgres, redis, coturn

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로 룸을 만든 화상회의.

댓글

아직 댓글이 없습니다.

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