WebRTC로 브라우저 간 파일 전송 구축: TransP2P를 구축하며 배운 점

이 글은 TransP2P가 어떻게 내부적으로 동작하는지에 대한 기술 심층 분석입니다 — WebRTC 내부 동작, 브라우저 스토리지 특징 등 기술 세부 사항이 포함됩니다. 사용 방법 가이드만 필요하시다면 대신 튜토리얼을 확인하세요.
요약: 나는 브라우저에서 완전히 실행되는 P2P 파일 전송 도구를 구축했습니다. 설치 필요 없음, 서버 중계 필요 없음, 계정 필요 없음. WebRTC 데이터 채널, 재개 가능한 전송, 그리고 브라우저 스토리지의 혼란스러운 영역에 대해 배운 점을 소개합니다.
대부분의 파일 전송 도구는 동일한 패턴을 따릅니다: 서버에 업로드, 링크 가져오기, 수신자가 해당 서버에서 다운로드. 최소한 일정 시간 동안 파일이 다른 사람의 인프라에 저장됩니다.
나는 다른 접근 방식을 시도하고 싶었습니다: 파일이 브라우저를 전혀 떠나지 않는다면 어떨까요?
이 질문이 TransP2P로 이어졌습니다 — WebRTC 기반의 브라우저 기반 P2P 파일 전송 도구입니다. 이 글에서는 기술적 결정, 예상보다 어려웠던 부분, 그리고 거의 아무도 이야기하지 않는 두 가지 브라우저 스토리지 경로에 대해 알아보겠습니다.
기본 아키텍처
이론적으로 흐름은 간단합니다:
- 두 기기 모두 사이트를 엽니다 (설치 필요 없음, 계정 필요 없음).
- WebSocket 시그널링 서버가 SDP 제안/응답과 ICE 후보를 교환하도록 도와줍니다.
- 직접 WebRTC 데이터 채널이 설정됩니다.
- 파일이 청크로 분할되어 데이터 채널을 통해 전송됩니다.
- 수신자는 청크를 재조합하고 파일을 저장합니다.
시그널링 서버는 파일 데이터에 절대 접근하지 않습니다. 연결이 설정되면 연결 유지만 도와줍니다.
모든 것을 청크화 — 필요하지 않을 때도
초기에 성과를 거둔 결정: 재개 가능한 전송이 활성화되었는지 여부에 관계없이 모든 파일이 전송 중에 청크화됩니다.
대안 — 파일을 단일 blob으로 보내는 것 —은 단순해 보이지만 빠르게 문제를 일으킵니다. 큰 파일은 메인 스레드를 차단하고, 네트워크 장애가 발생하면 처음부터 다시 시작해야 합니다.
청크를 사용하면 각 조각은 일반적으로 64 KB입니다. 송신자는 데이터 채널을 통해 청크를 스트리밍하고, 수신자는 이를 메모리에 버퍼링한 다음 다운로드 시 디스크에 씁니다. 이는 전송 자체 동안 중간 디스크 I/O가 없기 때문에 가장 빠른 속도를 제공합니다 — 병목 현상은 디스크가 아닌 데이터 채널 처리량입니다.
재개 가능한 전송 문제 (및 왜 두 가지 솔루션이 있는지)
재개 가능한 전송은 흥미로웠던 부분이었습니다 — 브라우저 차이로 인해 두 개의 완전히 분리된 구현이 필요했습니다.
목표
전송이 중단된 경우(네트워크가 끊어지거나 사용자가 탭을 닫은 경우), 수신자는 중단된 지점에서 재개할 수 있어야 하며, 영점부터 시작할 필요가 없습니다.
핵심 아이디어는 간단합니다: 어떤 청크가 수신되었는지 추적하고, 재연결 시 송신자에게 청크 N부터 시작하도록 알립니다.
그러나 이러한 청크를 어디에 저장할지가 어려운 부분입니다. 브라우저는 원시 디스크 액세스를 제공하지 않기 때문입니다. 그리고 모든 브라우저가 동일한 API를 제공하지는 않습니다.
경로 1: OPFS + 파일 시스템 액세스 API (Chrome, Edge, Firefox)
최신 Chromium 기반 브라우저와 최신 Firefox 버전은 Origin Private File System (OPFS)을 지원합니다. OPFS를 사용하면 샌드박스화된 오리진 프라이빗 디렉토리에 파일을 쓸 수 있으며, Web Worker와 함께 사용하면 메인 스레드 외부에서 쓰기 작업이 수행되어 빠릅니다.
제 구현:
- 파일 청크는 Web Worker의 writable stream을 사용하여 OPFS에 직접 씁니다. 이는 브라우저의 메모리 제한을 완전히 우회합니다 — 기가바이트 단위의 파일을 처리할 수 있습니다.
- 파일 메타데이터(청크 수, 수신된 청크 인덱스, 파일 이름, 총 크기)는 IndexedDB에 저장됩니다 — 작고, 구조화되며, 쿼리 가능하기 때문입니다.
재연결 시 수신자는 IndexedDB를 쿼리하여 해당 파일의 전송이 완료되었는지 확인합니다. 완료되지 않은 경우 시그널링 채널을 통해 마지막으로 성공적으로 수신된 청크 인덱스를 송신자에게 보냅니다. 송신자는 앞부분을 건너뛰고 해당 청크부터 시작합니다.
경로 2: IndexedDB만 사용 (Safari / iOS)
Safari는 Web Worker에서 OPFS에 접근하는 것을 지원하지 않으며, iOS에서는 파일 시스템 액세스 API가 전혀 사용할 수 없습니다. 즉, 메인 스레드 외부에서 디스크로 스트리밍할 수 없습니다.
Safari 사용자의 경우 대안은 모든 것을 IndexedDB로 사용하는 것입니다 — 파일 청크와 메타데이터 모두입니다.
이것은 작동하지만 한계가 있습니다. IndexedDB는 디스크에 플러시하기 전에 데이터를 메모리에 저장하므로 매우 큰 파일은 결국 Safari의 메모리 제한에 도달합니다. 특히 iOS에서는 제한이 엄격하여 파일 크기가 실제로 제한되며, 전송 시작 전에 크기 경고를 표시하여 경험을 우아하게 저하시킵니다.
이것은 이상적이지는 않지만 솔직합니다. 도구는 브라우저가 할 수 있는 것과 없는 것을 알려주며, 침묵 속에 실패하는 대신 사용자에게 알립니다.
시그널링: 최소화
시그널링 서버의 유일한 역할은 WebRTC 연결이 설정될 때까지 피어 간에 SDP와 ICE 후보를 중계하는 것입니다. 이를 위해 WebSocket을 사용합니다 — 간단하고 광범위하게 지원되며 지연 시간이 낮습니다.
서버는 사용자를 인증하지 않습니다(계정 필요 없음), 활성 세션을 넘어 메시지를 저장하지 않으며, 어떤 것이 전송되는지 알지 못합니다. 데이터 채널이 열리면 시그널링 서버는 전송과 관련이 없어집니다.
프로덕션 환경에서는 NAT 뒤의 피어가 연결할 수 있도록 STUN/TURN 서버가 필요합니다. UDP가 차단된 시나리오를 위해 공개 STUN 서버와 자체 호스팅한 TURN 서버를 조합하여 사용합니다.
브라우저 메모리 제한 우회
이것이 가장 놀라운 부분입니다: 데이터 채널 자체에는 메모리 문제가 없습니다 — 수신자의 스토리지 전략에 문제가 있는 것입니다.
2GB 파일을 수신하고 ArrayBuffer로 재조합하려고 하면 브라우저가 충돌합니다. 핵심은 수신된 청크를 메모리에 전체 파일을 보유하지 않고 직접 스토리지(OPFS 또는 IndexedDB)에 스트리밍하는 것입니다.
OPFS + Web Worker를 사용하면 이것이 아름답게 작동합니다 — 메인 스레드가 응답성을 유지하고 파일이 단일 메모리 내 객체로 존재하지 않습니다. 이것이 TransP2P가 지원되는 브라우저에서 다중 기가바이트 파일을 처리할 수 있는 이유입니다.
Safari와 iOS Safari는 이를 수행할 수 없습니다. 이것이 두 경로 문제로 돌아오는 이유입니다. "브라우저에서 작동함"과 "모든 브라우저에서 동등하게 작동함"은 두 가지 다른 진술입니다.
비동기 모드: P2P가 불가능할 때
두 피어가 동시에 온라인인 경우 P2P는 훌륭합니다. 하지만 아직 온라인이 아닌 사람에게 파일을 보내고 싶거나 연결이 계속 실패하는 경우도 있습니다.
이를 위해 TransP2P에는 선택적 서버 캐시 모드가 있습니다. 파일을 서버에 업로드하고(암호화되고, 서버에 전송되지 않는 무작위로 생성된 키를 사용), 다운로드 링크 또는 코드를 받은 후 수신자가 나중에 검색할 수 있습니다. 파일은 자동으로 만료됩니다 — 설정된 시간 후 또는 다운로드 횟수에 도달한 후 중 하나입니다.
이것은 서버 리소스를 소모하기 때문에 유료 기능이지만, 암호화 설계로 인해 서버는 파일 내용을 볼 수 없습니다. 암호화 키는 클라이언트 측에서 파생되어 다운로드 링크 프래그먼트(# 뒤의 부분)에 삽입되므로 서버에 전혀 도달하지 않습니다.
일찍 알았으면 좋았을 것들
ICE 후보 수집에 시간이 걸립니다. 연결이 멈춘 것으로 가정하지 마세요 — 종종 몇 초 더 필요합니다. 사용자에게 피드백을 표시하세요.
모바일 브라우저는 데스크톱 브라우저가 아닙니다. iOS Safari는 최신 브라우저 중 가장 엄격한 제한을 가지고 있습니다. 출시 후가 아니라 일찍 테스트하세요.
데이터 채널 신뢰성 대 속도는 트레이드오프입니다. ordered: true는 순서를 보장하지만 패킷 손실 시 정지할 수 있습니다. 파일 전송의 경우 ordered: true와 청크 수준 체크섬을 선택했습니다 — 실제로 정지는 드물고 순서 오류가 더 나쁘기 때문입니다.
사람들은 "P2P"를 이해하지 못합니다. 가장 큰 UX 과제는 기술적인 것이 아니었습니다 — 파일이 업로드되지 않고 전송되는 이유를 설명하는 것이었습니다. 비슷한 것을 구축한다면 "작동 원리" 복사에 시간을 투자하세요.
시도해보기 / 코드 보기
TransP2P는 무료로 사용할 수 있습니다(P2P 모드는 완전히 무료; 서버 캐시는 유료). 계정 필요 없음, 설치 필요 없음:
사이트에는 P2P 파일 전송이 실제로 어떻게 작동하는지와 클라우드 기반 파일 전송의 숨겨진 프라이버시 위험에 대한 몇 가지 긴 기사도 있습니다 — 기술적인 측면이 아닌 부분에 관심이 있는 경우 일반 독자를 위한 글입니다.
WebRTC 데이터 채널로 무언가를 구축한 적이 있다면 어떤 트레이드오프를 했는지 듣고 싶습니다 — 특히 큰 파일 처리에 대해. 2026년에도 브라우저 스토리지 환경은 여전히 혼란스럽고, 더 많은 현장 보고가 모두에게 도움이 될 것입니다.
이 글은 원래 TransP2P의 저자가 작성했습니다. P2P 도구 또는 브라우저 기반 파일 전송을 개발 중이라면 언제든지 연락해 주세요.