시리즈: distributed-p2p-protocol-deep-dive에 해당하는 글입니다.
![[P2P 설계] edge와 serverless를 P2P와 섞으면 무엇이 달라질까](/tistory/body-images/2026-07-04-p2p-protocol-09-edge-serverless-hybrid-p2p/cover.webp)
순수 P2P 그림은 매력적이다. 서버는 연결만 돕고 payload는 피어끼리 직접 보낸다. 다이어그램도 깔끔하고 비용 설명도 쉽다. 그런데 제품 화면으로 내려오면 금방 예외가 생긴다. 사용자는 동시에 접속하지 않을 수 있고, 모바일은 중간에 잠들 수 있고, 회사망에서는 TURN이 필요하고, 권한과 결제는 어딘가에서 확인해야 한다.
![[P2P 브라우저] 모바일과 백그라운드는 왜 P2P 전송을 흔들까](/tistory/body-images/2026-07-04-p2p-protocol-08-mobile-browser-background-limits/cover.webp)
데스크톱에서 파일 전송 데모가 잘 돌면 모바일도 비슷할 것 같았다. 같은 WebRTC이고 같은 DataChannel이면 차이는 화면 크기 정도라고 생각하기 쉽다. 실제로는 사용자가 잠깐 메신저를 확인하는 순간부터 조건이 바뀐다. 화면이 꺼지고, 탭이 뒤로 밀리고, OS는 배터리를 아끼고, 저장 권한은 브라우저마다 다르게 보인다. 브라우저만 있으면 나머지는 따라올 거라고 믿었다. 아래에서는 P2P을 중심으로, 깨진 가정과 다시 그은 경계를 순서대로 남긴다.
![[P2P 파일 무결성] chunk hash와 Merkle tree는 왜 필요할까](/tistory/body-images/2026-07-04-p2p-protocol-07-merkle-chunk-integrity/cover.webp)
파일 전송 화면에서 진행률이 96%까지 올라갔다. 사용자는 거의 끝났다고 느끼고, 개발자는 마지막 checksum만 맞으면 된다고 생각한다. 그런데 마지막 hash가 틀리면 제품은 갑자기 난처해진다. 어떤 조각이 깨졌는지 모르니 다시 받을 범위도 말하지 못한다. 로그 보존 기간도 작게나마 정해야 한다. hash 값 자체를 오래 남기는 것이 부담스럽다면, root version과 실패한 chunk index, retry 결과만 남길 수 있다. 사용자가 문의했을 때 필요한 것은 파일 내용이 아니라 어떤 상태에서 멈췄는지다.…
![[TURN 운영] TURN 서버 비용은 어떻게 계산해야 할까](/tistory/body-images/2026-07-04-p2p-protocol-06-turn-server-cost-model/cover.webp)
PonsWarp에서 큰 파일 하나를 보내는데 회사망과 모바일망이 섞여 있다고 해 보자. 화면에는 “연결 중”이라고만 보이지만, 내부에서는 direct가 실패하고 TURN relay로 넘어갈 수 있다. 사용자는 성공 여부와 속도를 본다. 운영자는 relay egress와 region 비용을 본다. **P2P 제품은 어느 순간부터 TURN을 예외가 아니라 비용 모델로 계산해야 할까?**
![[P2P 보안] P2P에서 trust boundary는 어디에 그어야 할까](/tistory/body-images/2026-07-04-p2p-protocol-05-p2p-security-trust-boundary/cover.webp)
PonsWarp에서 초대 링크를 눌러 파일을 받는 장면을 떠올려 본다. 브라우저끼리 직접 연결됐고, 채널은 암호화되어 있고, 서버를 덜 거쳤다. 그래도 안심하기에는 이르다. 상대 peer가 어떤 파일을 요청할 수 있는지, 방 안에서 어떤 행동을 할 수 있는지, 잘못된 요청을 얼마나 자주 보낼 수 있는지는 아직 따로 정해야 한다. **직접 연결이 열렸을 때 제품은 무엇을 믿고, 무엇을 끝까지 의심해야 할까?**
![[P2P 프로토콜] CRDT와 P2P 협업은 왜 같이 이야기될까](/tistory/body-images/2026-07-04-p2p-protocol-04-crdt-p2p-collaboration/cover.webp)
PonsLink 화이트보드에서 두 사람이 같은 도형을 고친다고 상상해 본다. 한 사람은 지하철 안에서 잠깐 오프라인이 되고, 다른 사람은 데스크톱에서 선을 지운다. 몇 분 뒤 두 브라우저가 다시 만나면 화면은 하나로 합쳐져야 한다. 여기서 어려운 질문은 “누가 이겼는가?”가 아니다. **서버가 모든 순서를 정하지 못할 때, 제품은 어떤 변경을 자동으로 합치고 어떤 변경을 설명해야 할까?**
![[P2P 프로토콜] WebTransport와 QUIC은 WebRTC를 대체할까](/tistory/body-images/2026-07-04-p2p-protocol-03-webtransport-quic-vs-webrtc/cover.webp)
대용량 파일을 보내는 화면을 만들고 있다고 생각한다. 첫 번째 시도는 브라우저끼리 직접 붙는다. 두 번째 시도는 서버를 거쳐 빠르게 붙는다. 둘 다 bytes는 흐를 수 있지만 사용자가 동의한 약속은 같지 않다. 실험 결과를 읽을 때도 마지막 성공 여부만 보지 않는다. direct 경로가 성공했지만 ICE 후보 수집에 오래 걸렸다면 사용자는 이미 불안해졌을 수 있다. 서버 endpoint는 빨리 붙었지만 relay byte가 크게 늘었다면 비용 모델이 바뀐다. TURN 전환이 잦다면 WebRTC를 고집한 것이 아니라…
![[P2P 프로토콜] libp2p는 라이브러리보다 경계 묶음에 가깝다](/tistory/body-images/2026-07-04-p2p-protocol-02-libp2p-stack-boundaries/cover.webp)
처음 libp2p를 봤을 때는 P2P를 한 번에 해결해 주는 큰 라이브러리처럼 읽었다. import하고 설정 몇 줄을 넣으면 peer가 생기고, 연결되고, 메시지가 흐를 것 같은 느낌이었다. libp2p가 답을 하나로 고정하지 않는다는 점은 장점이다. TCP, QUIC, WebSocket, WebTransport 같은 transport가 다르고, Noise 같은 보안 계층과 mplex/yamux 같은 stream 경계가 따로 있다. 대신 어떤 모듈을 왜 쓰는지 적지 않으면 선택 비용이 제품 뒤쪽에서 터진다.…
![[P2P 프로토콜] DHT와 BitTorrent식 peer discovery를 다시 보기](/tistory/body-images/2026-07-04-p2p-protocol-01-dht-bittorrent-peer-discovery/cover.webp)
파일 전송 버튼을 눌렀는데 전송률 그래프가 움직이지 않는 장면을 먼저 떠올린다. 사용자는 “느리다”고 말하지만, 실제로는 아직 보낼 상대를 찾지 못한 상태일 수 있다. bytes가 흐르기 전에도 P2P는 이미 실패할 수 있다. 아래에서는 P2P을 중심으로, 깨진 가정과 다시 그은 경계를 순서대로 남긴다.