[PonsWarp] 서버가 파일을 갖지 않는 전송을 만들고 싶었다
큰 파일을 보낼 때 사람들은 보통 클라우드를 떠올린다. 올리면 링크가 생기고, 상대가 받는다. 익숙하지만 대가는 분명하다. 서버가 파일을 잠시라도 갖게 되고, 권한·보관·삭제·비용 문제가 따라온다.
PonsWarp에서 붙잡은 질문은 단순했다. 서버가 파일을 소유하지 않는 전송을 만들 수 있는가.
가능은 하다. 다만 “서버가 빠진다”는 말이 “책임이 사라진다”는 뜻은 아니었다. 이 글은 P2P 낭만이 아니라, 서버를 비운 자리에 어떤 책임이 남는지에 대한 메모다.

서버가 파일을 갖지 않는 게 커진 문제
초기 현장에서 먼저 터진 것은 보안 철학이 아니라 체감 실패였다.
- 중간까지 가다가 멈춤
- 받는 쪽 메모리 압박
- 저장 API 차이로 완료 판단이 어긋남
- 재시도 시 처음부터 다시 보내는 피로
- 연결은 살아 있는데 전송만 정체
사용자는 “P2P라서 느린가?”보다 **“지금 뭐가 되고 있지?”**를 물었다. 서버에 파일이 없다는 설계를 이해시키기 전에, 진행과 실패를 설명하지 못하면 설계는 무의미해진다.
특히 대용량에서 잔인한 지점은 후반부다. 90%까지 간 전송이 저장 단계에서 죽으면, 사용자는 기술 세부보다 “거의 다 됐는데 사라짐”을 기억한다. 이 감정을 무시하고 아키텍처 우아함만 말하면 제품이 아니라 논문처럼 들린다.
현장 증상은 보안 체크리스트보다 먼저 제품 문장을 요구했다. “서버에 저장하지 않습니다”는 좋은 문장이지만, 그 순간 사용자가 듣고 싶은 말은 “지금은 저장 확인 중이고, 실패하면 어디부터 다시 하면 됩니다”에 더 가깝다. 철학이 경험을 앞서가면 신뢰가 깎인다.

처음에는 서버가 파일을 들고 있어야 할 줄 알았다
첫 설계의 매력은 명확했다. signaling만 서버가 돕고, 파일 바이트는 DataChannel로 직접 보낸다. 저장도 브라우저가 한다. 서버 디스크가 없으니 보관 정책이 단순해 보였다.
놓친 것은 이것이다.
- signaling 서버는 소개자이지 무책임자가 아니다
- TURN relay가 개입하는 순간 경로 설명 책임이 생긴다
- 송신·수신·저장은 서로 다른 속도로 움직인다
- “서버에 없음”을 증명하려면 관측 포인트가 더 필요해진다
- 브라우저 탭 생명주기가 전송 생명주기를 위협한다
서버를 빼면 구현이 줄지 않는다. 책임이 브라우저와 프로토콜 경계로 이동할 뿐이다. 오히려 운영자는 “파일이 어디 있는지”를 서버 로그로 확인할 수 없으니, 상태 가시성을 더 정교하게 설계해야 한다.
첫 설계가 틀린 지점은 P2P 자체에 대한 과도한 낭만이었다. 직접 경로가 가능하다는 사실과, 사용자가 그 경로를 신뢰할 수 있다는 사실은 다르다. 신뢰는 속도 숫자보다 예측 가능한 실패 처리에서 나온다. 그래서 설계 문서를 다시 쓸 때는 “어떻게 보내는가”보다 “어떻게 설명하는가”를 위에 두었다.
서버 비소유가 선명해진 순간
로그에서 반복된 반례는 “연결은 살아 있는데 전송만 죽은” 경우였다. peer connection state만 보면 정상처럼 보여도, backpressure나 수신 측 저장 지연으로 송신 큐가 막혔다.
속도 최적화보다 먼저 필요한 것은 흐름 제어였다. ACK 없는 전송, 윈도 없는 전송, 저장 완료 없는 완료 표시는 모두 같은 배신으로 끝났다.
반례를 정리하면 대략 세 층이다.
- 연결 층: ICE/DTLS/DataChannel open
- 전송 층: chunk 송신, window, retransmit
- 저장 층: filesystem write, stream save, final close
사용자는 세 층을 하나의 진행률로 본다. 제품이 층을 구분하지 않으면, 어느 층이 멈췄는지 안내할 수 없다. 그래서 내부 메트릭과 외부 문장을 같은 세 층으로 맞추는 작업이 필요했다.
로그를 보고 놀랐던 점은, 실패의 다수가 “완전 단절”이 아니라 “부분 진행 후 정체”였다는 사실이다. 스피너 하나로 둘을 같게 취급하면 사용자는 언제 기다려야 하고 언제 포기해야 하는지 모른다.
서버 경계 나누기
다시 그은 경계는 이렇게 정리됐다.
| 구성요소 | 가져야 할 책임 | 갖지 않아야 할 책임 |
|---|---|---|
| Signaling | 매칭, 재연결 힌트 | 파일 바이트 보관 |
| TURN | 연결 보조 | 영구 스토리지 |
| Sender | 청크·윈도·재시도 | “무조건 도착” 보장 과장 |
| Receiver | 저장·검증·완료 ACK | 송신 속도 강제 |
| 제품 UI | 경로/상태 설명 | 침묵하는 스피너 |
“서버가 파일을 갖지 않는다”는 문장은 마케팅 문장이기 전에 저장 책임의 위치 선언이어야 한다. 그 선언이 진짜가 되려면, 완료 조건을 송신 종료가 아니라 수신 확인으로 고정해야 한다.
또한 직접 경로가 불가능할 때의 대안 문장도 필요하다. 무조건 P2P만 고집하면, 실패 시 사용자가 선택할 다음 행동이 없다. 제품은 이념이 아니라 경로 선택지를 팔아야 한다. 선택지가 정직할수록 “서버 미보관” 약속도 설득력을 얻는다.
책임을 옮긴다는 건 누군가를 탓하자는 뜻이 아니다. 장애가 났을 때 어느 레이어의 문장으로 복구 가이드를 쓸지 정하자는 뜻이다. 경계가 흐리면 모든 실패가 제품 전체의 실패가 되고, 고치는 손도 분산된다.
구현과 운영에 남긴 조건
- 완료는 송신 종료가 아니라 수신 저장 확인 후다
- 실패 문장은 연결/전송/저장을 구분해 쓴다
- 브라우저 제약(특히 Safari)을 숨기지 않는다
- 대용량일수록 속도 숫자보다 제어 가능성을 먼저 보여 준다
- 재시도는 “처음부터”가 기본값이 되지 않게 설계한다
이 규칙이 있어야 P2P 전송이 “신기한 데모”가 아니라 운영 가능한 도구가 된다. 보안 장점(서버 미보관)은 신뢰 가능한 완료 경험 위에 올려야만 설득력을 가진다.
운영 규칙은 릴리즈 체크리스트에도 넣었다. 새 전송 최적화가 상태 문장을 뭉개지 않는지, 완료 이벤트가 저장 확인 전으로 앞당겨지지 않는지, 실패 코드가 세 층 중 어디에 매핑되는지. 성능 PR이 신뢰 PR을 덮어쓰지 않게 하는 장치다.
아직 열어 둔 비용
서버 미보관 설계가 진짜인지 보려면 아키텍처 그림보다 아래 문장 테스트를 하는 편이 빠르다.
- 완료를 송신 종료로 말하고 있지는 않은가
- 실패를 “전송 실패” 한 줄로 뭉개고 있지는 않은가
- TURN 구간을 존재하지 않는 것처럼 숨기고 있지는 않은가
- 브라우저 제약을 “사용자 환경 문제”로만 돌리고 있지는 않은가
- 재시도가 항상 처음부터인지, 이어서인지 사용자가 아는가
하나라도 애매하면 서버를 비운 게 아니라 설명을 비운 것이다. PonsWarp의 핵심 경쟁력은 바이트 경로의 우아함이 아니라, 그 경로를 사용자가 견딜 수 있게 만드는 문장이다. 서버 미보관 전송을 설명하는 가장 나쁜 방법은 기술 약어를 나열하는 것이다. DataChannel, ICE, TURN, StreamSaver를 줄줄 읽으면 엔지니어에게는 명확해 보여도 사용자에게는 안심이 안 된다. 필요한 것은 경로의 이름보다 현재 단계의 의미다.
내가 쓰는 사용자 단계 모델은 단순하다.
- 상대와 전송 준비를 맞추는 중
- 파일을 조각으로 보내는 중
- 상대 기기에 저장하는 중
- 저장이 확인되어 완료
이 네 단계만 화면에 정직하면, 서버가 없는 설계도 이해된다. 반대로 네 단계를 한 스피너로 합치면, 서버가 있든 없든 불안하다. 아키텍처의 우아함은 단계 모델 위에 올려야 빛난다.
테스트 전략도 바뀌었다. 단위 테스트로 chunk encode를 검증하는 일만큼, 브라우저 매트릭스에서 저장 완료 이벤트가 언제 발생하는지 보는 일이 중요해졌다. 특히 Safari는 “보낼 수 있음”과 “사용자가 결과 파일을 확실히 가짐” 사이의 간극이 크다. 그 간극을 제품이 메우지 않으면, 서버 미보관의 이점은 체감되지 않는다.
운영 중 가장 조심한 문장은 “안전하게 전송됩니다” 같은 총칭이었다. 안전의 의미가 암호화인지, 서버 미보관인지, 완료 보장인지 불명확하면 과대 광고가 된다. 가능하면 보장 범위를 쪼개 말한다. 연결 보호, 서버 미보관, 완료 확인. 각각을 따로 약속해야 따로 지킬 수 있다. 서버 미보관을 제품 가치로 내세울수록, 관측 가능성과 사용자 교육 비용이 따라온다. 무료 티어 트래픽 절감 같은 부수 효과는 있어도, 핵심 가치 제안은 “당신이 보낸 파일이 우리 디스크에 남지 않는다”와 “그래도 완료를 확인한다”의 쌍이어야 한다. 한쪽만 강조하면 불안하거나 허황되다.
그래서 나는 전송 제품을 보안 제품처럼만 포장하지 않으려 한다. 보안은 강한 축이지만, 매일의 사용 이유는 예측 가능한 전송 경험이다. 그 경험이 먼저 서고, 서버 미보관이 그 위에 얹혀야 한다.
직접 전송 경로를 지키려는 집착과, 사용자에게 다음 행동을 주는 유연함 사이에서 나는 후자를 고르려 한다. 경로의 순수함보다 완료의 명확함이 우선이다. 순수함은 엔지니어의 만족이고, 명확함은 사용자의 신뢰다. 같은 결론을 다음 주 우선순위 회의에서도 다시 꺼내 보려 한다.
직접 경로가 막힐 때를 대비한 안내 한 줄이, 화려한 전송 애니메이션보다 제품을 더 오래 살린다.
마치며
서버가 파일을 갖지 않는 전송은 매력적인 목표다. 다만 서버를 비우는 순간, 제품은 더 많은 상태를 사용자 앞에 펼쳐야 한다.
PonsWarp에서 배운 한 줄은 이것이다. 파일을 갖지 않는 서버를 만들려면, 설명을 가지는 제품을 먼저 만들어야 한다.
서버를 빼는 설계는 책임 제거가 아니라 책임 이동이다. 그 이동을 정직하게 보여 주는 쪽이 결국 더 강한 전송 제품이 된다. 다음에도 같은 목표를 본다면, 아키텍처 그림보다 상태 문장 표를 먼저 쓰겠다.