<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 W4] PonsWarp Grid — TS로 뽑아낸 P2P 엔진과 코디네이터

Declan·2026년 8월 28일

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

브라우저를 떠난 엔진. PonsWarpEngine, per-piece SHA-256, OPFS 폴백, coordinator planning 분리로 CLI까지 뽑은 TS 엔진.

# [Blueprint W4] PonsWarp Grid — TS로 뽑아낸 P2P 엔진과 코디네이터 > Series: `blueprint-ponswarp` (4/5) · Category: `P2P/분산기술` + `개발 회고` · Slug: `blueprint-ponswarp-04-grid-engine` > Source: `blueprints/specs/P01_ponswarp_family.md` §04 + `blueprints/04-ponswarp-grid.png` + `ponswarp-grid/packages/core` — ## 0. TL;DR `ponswarp`가 브라우저에 종속된 전송이었다면, `grid`는 브라우저를 떠난 엔진이다. `PonsWarpEngine(createSession/joinSession)`이 `piece` 단위로 파일을 쪼개 `per-piece SHA-256`으로 검증하고, `OPFS→IndexedDB→Memory`로 resume한다. `coordinator`는 바이트를 안 만지고 `후보 계획`만 짠다. `Rust/WASM` 없이 순수 TS로 만든 이유: `pnpm`으로 `CLI`까지 뽑기 위해.
PonsWarp Grid 설계도 04
— ## 1. 문제 정의 — 왜 엔진을 추출했나 `ponswarp`와 `vnext`는 모두 브라우저 SPA에 묶여 있었다. 그러나 `CLI`에서 `send/join`, `node start`로 시딩하고, `Tauri`에서 `warp-agentd`로 띄우려면, 전송 로직이 브라우저 밖에서도 돌아야 했다. `pons-core-wasm`은 브라우저 전용이라 `Node`에서 못 썼다. 그래서 `grid`는 `Rust`를 버리고 `TS`로 다시 썼다. `grid.ponslink.com`이 그 결과. — ## 2. 설계도 한눈에 — piece와 스토리지 폴백 중앙 `packages/core: PonsWarpEngine`. 왼쪽 `webrtc`, 오른쪽 `signaling(JS WS :8787)`, 아래 스토리지 3단. * **piece 흐름:** `createSession`이 파일을 `piece(256KB~1MB)`로 쪼개 `manifest`를 만든다. `joinSession`은 `manifest`를 받아 `per-piece SHA-256` 게이트를 통과한 조각만 `OPFS`에 쓴다. 손상된 piece는 빨간 화살표로 `폐기→재요청`. * **스토리지:** `OPFS → IndexedDB → Memory` 폴백. `OPFS`가 있으면 파일 핸들로 쓰고, 없으면 `IDB`에 `ArrayBuffer`로 쌓고, 그것도 없으면 메모리에 둔다. `safeAssembleBytes`가 `256MiB`를 기준으로 `Blob` vs `writable stream`을 가른다. * **coordinator:** `POST /api/grid/v1/*`로 `메타 등록·후보 계획`, `ed25519` 노드 등록. `ponswarp://join` URI로 참여. coordinator는 `누가 어떤 piece를 가졌는지`만 알고, 바이트는 피어끼리 `DataChannel`로 간다. * **CLI:** `packages/cli(send/join/node start/publish/files/download)`가 `core`를 감싼다. `apps/demo(Vite)`가 브라우저 데모. — ## 3. 아키텍처 해부 — 3개 패키지 “` ponswarp-grid/ ├── packages/core # PonsWarpEngine, piece 관리, SHA-256 ├── packages/webrtc # DataChannel 래퍼 ├── packages/signaling # JS WS :8787 ├── packages/react # 훅 └── packages/cli # send/join/node “` * **core:** `createSession(file) → manifest`, `joinSession(manifest) → session`. `seed-after-complete` — 비소유자도 받은 piece를 provider로 제공한다. * **signaling:** `Node WS`가 `offer/answer/ICE`만 중계. Rust Axum과 프로토콜은 다르지만, 역할은 동일. * **coordinator:** `ed25519` 키로 노드를 등록. `planning`은 `coordinator`가, `byte transport`는 `DataChannel`이. 분리 덕에 coordinator는 가볍다. — ## 4. 로직 딥다이브 — 2개만 깊게 ### 4.1 per-piece SHA-256 게이트 “`ts const hash = await sha256(piece.bytes); if (hash !== manifest.pieces[seq].sha256) { discard(piece); requestRetransmit(seq); } “` * 파일 전체 해시가 아니라 `piece`마다 해시. 손상된 piece만 버리고 재요청. `128MB` 파일의 1개 piece가 깨져도 전체를 버리지 않는다. ### 4.2 safeAssembleBytes 256MiB 임계 * `assembleBytes < 256MiB`: `Blob`으로 합쳐 `URL.createObjectURL`로 다운로드. * `>= 256MiB`: `File System Access API`의 `writable stream`으로 스트리밍 저장. 브라우저 힙을 500MB 이하로 유지하는 `perf:500mb` 검증 통과. — ## 5. 왜 이 선택인가 | 선택 | 채택 | 버린 대안 | 버린 이유 | |—|—|—|—| | 코어 | 순수 TS | Rust/WASM 재사용 | `Node CLI`와 `Tauri`에서 `WASM` 로드가 꼼꼼, `pnpm` 패키지로 뽑기 어려움 | | 스토리지 | OPFS→IDB→Memory 3단 | OPFS 단일 | `Safari`·`Firefox`에서 `OPFS` 미지원·버그 | | coordinator | planning vs transport 분리 | coordinator가 바이트 중계 | 대역폭 비용, 단일 장애점 | — ## 6. 실패와 복구 * **실패:** 처음엔 `piece`를 `64KB`로 고정. `per-piece SHA-256` 검증 오버헤드가 커서 1GB 파일에서 16k번 해시. `256KB~1MB`로 가변으로 바꿔 오버헤드 1/4로 감소. * **복구:** `perf:500mb` 힙 예산 테스트에서 `Blob` 조립이 500MB를 넘기자 `OOM`. `safeAssembleBytes` 임계를 도입해 스트리밍으로 전환. — ## 7. 운영 증거 * `pnpm test` — `per-piece SHA-256`, `TURN strict-relay QA`, `heap 500mb` 검증 * `coordinator /api/grid/v1` — `ed25519` 노드 등록, `ponswarp://join` URI, `apps/demo` Vite 데모 * `Docker` — `JS WS :8787` + `coordinator` + `demo` 3종 — ## 8. 다음 편 예고 **W5: `ponswarp-desktop` — WarpInfra.** `Tauri2` + `Rust warp-core` + `QUIC mesh` + `mDNS` + `rarest-first` 스케줄링. 브라우저를 떠난 P2P가 인트라넷에서 어떻게 시딩하는지 다룬다. *부록: `blueprints/04-ponswarp-grid.png` 프롬프트 `prompts.part1.json#04`, 코드 진입점 `ponswarp-grid/packages/core/src/engine.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 L1] PonsLink P2P Room — WebRTC 화상회의를 BFF로 감싼 이유

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

댓글

아직 댓글이 없습니다.

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