[WarpInfra] 레지스트리 옆 CLI release get — 복제하지 않는 니치
"분산 파일 전송은 이미 시장에 있는데 왜 또 만들어?" 이 질문에 대한 답을 못 찾으면 WarpInfra는 시작도 하면 안 됐다. Resilio, Aspera, Signiant라는 검증된 상용 제품이 있고, 오픈소스 대안도 있다. 그런데 사내 인프라를 운영하다 보면 이 제품들을 쓰지 못하는 아주 구체적인 이유가 남는다. 이 편은 그 이유를 좁혀서, 결국 "레지스트리 옆에서 release를 받는 CLI"라는 한 좁은 일감만 남기기까지의 판단을 기록한다.
사내 배포가 무너지는 네 단계
먼저 해결하려는 문제가 실제로 어떻게 생기는지부터 보자. 사내 배포는 제품마다 조금씩 다르지만, 무너지는 패턴은 늘 같은 순서다.
- 빌드 아티팩트가 레지스트리에 올라간다.
- 받는 쪽은 HTTP로 큰 파일을 내려받느라 멈춘다. 수백 MB 바이너리를 열몇 대가 동시에 받으면 전송이 끝나기를 기다리는 시간이 배포 시간이 된다.
- 여러 머신이 같은 파일을 동시에 요청하면 레지스트리 서버 대역폭이 바닥난다. 서버를 증설하면 되지만, 그건 배포 문제의 해법이 아니라 비용의 전가다.
- 받고 나서도 무결성 검증이 없어서 "깨진 바이너리"가 퍼진다. 어느 머신이 잘못된 바이너리를 받았는지 역추적하는 일이 또 다른 작업이 된다.
이 네 단계 중 어느 하나도 새로운 문제가 아니다. 세상의 모든 사내 인프라 팀이 겪는 문제고, 상용 제품들도 결국 이 문제를 팔기 위해 존재한다. 문제는 기존 상용 제품이 이걸 푸는 방식이었다. 그리고 네 단계 중 마지막 — 무결성 — 은 흥미롭게도 기존 제품들도 반쪽만 푼다. 전송 완료를 알려 주지만 "받은 바이트가 보낸 바이트와 같은가"의 판정을 일급 기능으로 두지 않는 경우가 많다. 체크섬은 문서 어딘가에 있지만, 받는 쪽 파이프라인이 자동으로 판정받을 수 있는 계약은 아니다. WarpInfra가 SHA-256을 제품 정의 문장 안에 박아 넣은 건 이 갭을 겨냥한 것이다.
기존 제품이 푸는 방식과 그 무게
Resilio나 Aspera는 이 문제를 "전체 동기화 제품"으로 푼다. 폴더를 통째로 미러링하고, 클라우드 서비스를 경유하고, 엔터프라이즈 라이선스로 판매한다. 대규모 조직에는 맞는 설계다. 하지만 사내 미드마켓 환경에서는 그 무게 자체가 진입 장벽이었다.
세 가지가 부담이었다. 클라우드 강제 — 우리의 본무대는 사내망(인트라넷)인데, 제품이 클라우드 서비스를 전제로 설계돼 있으면 온프레 환경에서 절반은 쓸모가 없어진다. 라이선스 — 엔터프라이즈 가격 구조는 소규모 사내 배포에 맞지 않는다. 온프레 제약 — 방화벽 안에서만 동작해야 하는 환경에서 외부 의존이 있는 제품은 예외 처리의 온상이 된다.
정리하면 이렇다. 문제는 "전송 기술이 없다"가 아니라 "우리 환경에 맞는 최소 구성이 없다"였다. 이 구분이 중요한 이유는, 전자의 답이 "더 나은 전송 프로토콜 발명"이라면 후자의 답은 "검증된 조각의 최소 조합"이기 때문이다. WarpInfra가 새로 발명한 프로토콜은 없다. QUIC, SHA-256, piece 분산 — 다 있는 부품이다. 새로운 건 부품을 어떤 경계로 조립했는가다.
환경 가정을 먼저 고정한다
이 결정 뒤에는 제품이 살 환경에 대한 가정이 깔려 있다. 사내 미드마켓 인프라 환경의 특징을 굳이 나열하면 이렇다. 방화벽 안이 기본값이고, 클라우드 서비스 도입에 조직적 마찰이 있고, 머신 대수는 열 단위에서 수십 단위이고, 전송 대상은 빌드 산출물·데이터 세트·릴리스 아카이브 같은 "크고 덩어리진" 파일이고, 운영 인력은 소수다.
이 가정이 바뀌면 결정도 달라진다. 클라우드가 기본값인 환경이라면 객체 스토리지 + CDN 조합이 정답이고 그리드가 필요 없을 수 있다. 머신이 수천 대라면 기존 상용 제품의 무게도 정당화된다. 즉 이 편의 모든 기각은 "모든 환경에서 틀렸다"가 아니라 "이 환경 가정에서 틀렸다"다. 환경 가정을 글에 명시해 두는 이유가 이것이다. 가정이 바뀌면 기각을 다시 검토할 수 있게.
흥미로운 점은 이 환경 가정이 경쟁 제품의 설계 가정과 정확히 어긋난다는 것이다. 상용 제품들이 최적화한 방향 — 대규모 WAN, 클라우드 중심, 엔터프라이즈 거버넌스 — 은 우리 환경의 정반대다. 갭이 있는 지점은 대체로 기업들이 신경 쓰지 않는 지점이고, 신경 쓰지 않는 이유는 그 시장이 작아서다. 니치란 결국 "다른 회사가 의도적으로 비워 둔 자리"를 고르는 일이다.
설계 결정 — 복제 금지
선택지를 나열하고 하나를 남기는 방식으로 결정했다.
| 선택지 | 채택? | 이유 |
|---|---|---|
| Resilio/Aspera 통째 복제 | ❌ | 무겁고, 온프레 미드마켓에 과한 기능 |
| 클라우드 필수 동기화 | ❌ | 사내망(인트라넷)이 본무대 |
| 레지스트리 옆 CLI release get | ✅ | 좁은 일감, CLI 1등 시민, 온프레 |
이 표에서 중요한 건 첫 행의 기각이다. "비슷한 걸 가볍게 만들면 되지 않나"가 가장 유혹적인 길이었다. 폴더 미러링, 실시간 동기화, 버전 관리 — 기존 제품의 기능 목록을 하나씩 훑으면 "이것도 있으면 좋겠다"가 계속 나온다. 하지만 기능 목록을 복제하는 순간 유지보수 비용도 함께 복제된다. 포지셔닝 문서에 이 원칙이 고정돼 있다. "복제 금지: Resilio/Aspera/Signiant를 통째로 베끼지 않는다." 경쟁 분석 문서(docs/07-market-analysis-competitors-and-gaps.md, docs/08-3c-4p-critical-analysis-and-gtm.md)를 만든 목적은 기능 목록을 베끼기 위해서가 아니라, 어디가 갭인지 확인해서 갭만 패기 위해서였다. 시장 조사의 산출물이 기능 로드맵이 아니라 기각 근거가 되는, 다소 드문 방향의 문서다.
니치를 정의하는 네 경계
니치는 최종적으로 이렇게 정의됐다. "레지스트리에 올라간 release를, 사내망에서 여러 Node가 조각을 나눠 받아 SHA-256으로 검증하는 인프라." 이 문장에는 네 가지 경계가 들어 있다.
대상은 release다. 폴더 전체 동기화가 아니라, 레지스트리에 버전으로 올라간 아티팩트를 받는 일감만 한다. 실시간 협업 편집 같은 기능은 애초에 문제 정의에 없다. 공간은 사내망이다. 클라우드 경유를 전제로 하지 않으니, 방화벽 안에서 완결되는 것이 기본 동작이고 클라우드가 옵션이다. 방식은 조각 분산이다. 한 대가 서버에서 받는 게 아니라 여러 Node가 조각을 나눠 받으니, 서버 대역폭이 아니라 그리드 전체 대역폭이 처리량의 상한이다. 판정은 SHA-256이다. 전송 완료는 UI 표시가 아니라 해시 검증으로 판정한다 — "깨진 바이너리가 퍼진다"는 네 번째 무너짐 단계에 대한 직접 대응이다.
이 네 경계 밖의 기능 제안은 전부 "나중에"로 보낸다. 나중이 오는 조건도 정해져 있다. 데이터 그리드가 안정화되고, 20GB multi-host 증거가 나온 뒤에만 다음 니치를 연다. 이 조건부 개방은 로드맵 편의 주제다.
좁은 니치의 대가
니치가 좁으면 설계는 단순해지지만, 대신 "왜 굳이 새로 만드나"라는 질문에 계속 답해야 한다. 이 질문은 한 번 답하면 끝나는 게 아니라, 구현 단계에서 선택마다 다시 나온다. GUI와 agentd 중 무엇을 먼저 만들까 — 니치 정의가 "파이프라인이 운영자"라고 답한다. coordinator가 파일을 중계할까 — 니치 정의가 "사내망에서 조각을 나눠 받는다"라고 답한다. 컴퓨트 기능을 얹을까 — 니치 정의가 "release를 받아 검증하는 인프라"라고 답한다.
CLI를 1등 시민으로 둔 이유(S1-04), coordinator에서 파일 바이트를 막은 이유(S1-02), 컴퓨트 그리드를 뒤로 미룬 이유(S1-03) — 전부 이 니치 정의에서 파생된 판단이다. 설계 문서 한 장(docs/01-warp-infra-vision-and-naming.md)이 이후 146개 커밋의 방향을 계속 걸러 준 셈이다.
복제 금지 원칙의 실제 효용은 개발 중에 나왔다. 기능 요청이 오면 답이 두 갈래로 나뉜다. 네 경계 안의 것은 구현 순위를 논하고, 경계 밖의 것은 문서의 문장 하나를 가리킨다. 논쟁이 사라진 건 아니지만 논쟁의 길이가 짧아졌다. "실시간 폴더 동기화도 해주면 안 되나"라는 제안에 대해 성능을 논할 필요가 없다. "release가 아니라 폴더 동기화다"라는 경계 위반 하나로 끝난다. 좁은 니치는 거절의 근거를 무료로 공급한다.
이 규율이 실제 구현에서도 유지됐는지는 커밋 이력이 증언한다. Phase A의 커밋 목록을 보면 CLI, manifest, QUIC piece 경로, resume — 전부 네 경계 안의 부품이다. 동기화 UI, 클라우드 커넥터, 공유 워크스페이스 같은 기능은 커밋 하나 없다. 문서의 거절이 146개 커밋을 지나 살아남았다는 것, 그게 이 편에서 확인하고 싶었던 사실이다. 대신 깨지기 쉬운 지점도 있다. 니치 정의 자체가 틀렸을 때 — 즉 사내 미드마켓이 정말로 "release 분산 수신"이 아니라 "전체 동기화"를 원했을 때 — 이 제품은 정확하게 필요 없는 것을 만든 것이다. 그래서 니치 검증은 문서가 아니라 실측으로 해야 한다. RC1 게이트의 "20GB 실제 다중 머신 전송 증거 없으면 RC1 아님" 원칙이 그것이다. 니치가 실제 문제를 맞췄는지는 우리가 말하는 게 아니라 실측이 말한다. 사내 파일럿 시나리오(1 seed → 5 receivers)가 백로그 P0에 있는 이유도 같은 맥락이다. 그 검증은 아직 앞에 있다.
정리하면 이 편의 결정은 이렇다. 시장에 있는 제품을 복제하지 않고, 무너지는 네 단계 중 우리 환경에서 실제로 무너지는 부분만 골라, 네 경계로 좁게 정의하고, 검증 장치를 문서로 박아뒀다. 다음 편에서는 이 좁은 제품 안에서 가장 먼저 고정한 아키텍처 원칙 — 파일 바이트가 어디를 지나지 말아야 하는지 — 를 다룬다.
이 시리즈
- 이전: 시리즈 지도
- 다음: 파일 바이트는 Control을 지나지 않는다
- 목차: PonsWarp 개발 회고