Git을 데이터베이스로 사용하는 패키지 관리자들, 결국 실패한다
(nesbitt.io)- 여러 패키지 관리자들이 버전 관리와 협업의 편리함 때문에 Git을 데이터베이스처럼 사용했지만, 규모가 커질수록 성능과 유지보수 문제에 부딪힘
- Cargo, Homebrew, CocoaPods 등은 Git 인덱스의 크기 증가와 느린 업데이트 속도, CI 환경의 비효율로 인해 결국 HTTP 기반 인덱스나 CDN으로 전환
- vcpkg는 여전히 Git 트리 해시를 기반으로 동작하며, 얕은 클론(shallow clone) 환경에서 빌드 실패와 복잡한 우회 방법이 발생
- Go 모듈 시스템은 GOPROXY와 체크섬 데이터베이스(sumdb) 를 도입해 Git 의존성을 제거하고 보안성과 속도를 개선
- Git은 코드 협업에는 탁월하지만, 패키지 메타데이터 질의나 대규모 레지스트리 관리에는 부적합하다는 점이 반복적으로 드러남
Git을 데이터베이스로 사용하는 시도의 반복적 실패
- Git은 버전 이력, 분산 구조, 무료 호스팅 등의 장점으로 매력적이지만, 데이터베이스로 사용하면 확장성 한계에 부딪힘
- 여러 패키지 관리자가 Git을 인덱스로 채택했으나, 시간이 지나며 성능 저하와 인프라 부담이 심화됨
Cargo
-
crates.io 인덱스는 Git 저장소로 시작했으며, 모든 클라이언트가 전체 복제(clone)를 수행
- 저장소가 커지면서 delta resolution 단계에서 libgit2의 성능 병목이 발생
- CI 환경에서는 매 빌드마다 전체 인덱스를 다운로드해 낭비 심각
-
RFC 2789를 통해 sparse HTTP 프로토콜이 도입되어 필요한 메타데이터만 HTTPS로 가져오도록 개선
- 2025년 4월 기준, 요청의 99%가 sparse 모드 사용
- Git 인덱스는 여전히 존재하지만 대부분의 사용자는 접근하지 않음
Homebrew
- GitHub은 Homebrew에 얕은 클론 사용 중단을 요청, 업데이트가 “매우 비싼 연산”으로 지적됨
- homebrew-core의
.git폴더는 1GB에 육박, 업데이트 시 delta resolution으로 지연 발생
- homebrew-core의
- 2023년 2월 Homebrew 4.0.0에서 tap 업데이트를 JSON 다운로드 방식으로 전환
- Git fetch 제거로 업데이트 속도 향상, 자동 업데이트 주기도 5분에서 24시간으로 변경
CocoaPods
- iOS/macOS용 패키지 관리자 CocoaPods는 수십만 개의 podspec으로 구성된 Specs 저장소가 지나치게 커짐
- 클론 및 업데이트에 수분 소요, CI 시간 대부분이 Git 연산에 소비
- GitHub은 CPU rate limit을 적용, shallow clone이 서버 부하의 원인으로 지목
- 팀은 자동 fetch 중단, 풀 클론 전환, 저장소 샤딩 등 임시 조치 시행
- 1.8 버전부터 CDN 기반 HTTP 배포로 전환, 사용자 디스크 공간 약 1GB 절약, 설치 속도 대폭 향상
Nixpkgs
- Nix는 클라이언트 측에서 이미 tarball 기반 채널을 사용해 Git 복제를 피함
- 패키지 표현식은 S3와 CDN에서 HTTP로 제공
- 그러나 GitHub의 인프라가 83GB 규모의 저장소와 20,000개 포크로 인해 부담
- 2025년 11월, GitHub은 복제 간 합의 실패 및 유지보수 작업 오류를 보고
- 로컬 클론은 2.5GB지만, 포크 네트워크 전체가 GitHub 저장 공간을 압박
vcpkg
- Microsoft의 C++ 패키지 관리자 vcpkg는 Git 트리 해시로 버전 관리
-
builtin-baseline을 통해 특정 커밋 시점의 포트를 재현하려면 전체 히스토리가 필요
-
-
얕은 클론 환경(GitHub Actions, DevContainers) 에서는 빌드 실패 발생
- 해결책으로
fetch-depth: 0설정 필요, 전체 히스토리 다운로드 요구
- 해결책으로
- Git 트리 해시 구조상 커밋 추적 불가, 구조적 한계로 인한 수정 불가능
- 여전히 Git 저장소 기반 레지스트리만 지원, HTTP나 CDN 대안 없음
Go 모듈 시스템
- Grab 엔지니어링 팀은 모듈 프록시 도입 후
go get시간이 18분 → 12초로 단축 - 기존 방식은 각 의존성의 저장소 전체를 클론해야
go.mod를 읽을 수 있었음 - Go 팀은 VCS 도구 의존성과 보안 취약점을 우려
-
Go 1.13부터 GOPROXY가 기본, 모듈 소스와
go.mod를 HTTP로 제공- sumdb(체크섬 데이터베이스) 로 모듈 무결성과 영속성 보장
Git을 데이터베이스로 사용할 때의 일반적 문제
-
Git 기반 위키(Gollum) 는 대규모 저장소에서 디렉터리 탐색과 페이지 로딩이 느려짐
- GitLab은 Gollum 사용 중단 계획
-
Git 기반 CMS(Decap) 는 GitHub API 요청 한도(5,000회)에 걸림
- 약 10,000개 항목 이상에서 성능 저하, 빈 캐시 상태의 신규 사용자는 요청 폭주
-
GitOps 도구(ArgoCD) 는 저장소 클론 시 디스크 공간 초과 문제 발생
- 단일 커밋이 전체 캐시를 무효화, 대형 모노레포는 별도 스케일링 필요
Git이 데이터베이스로 부적합한 구조적 이유
-
디렉터리 한계: 파일 수가 많아질수록 느려짐
- CocoaPods는 16,000개 디렉터리로 인해 거대한 트리 객체 생성, 해시 기반 샤딩으로 해결
-
대소문자 구분 문제: Git은 구분하지만 macOS·Windows는 비구분
- Azure DevOps는 충돌 방지를 위해 서버 측 차단 기능 추가
-
경로 길이 제한: Windows의 260자 제한으로
git status오류 발생 -
데이터베이스 기능 부재:
- CHECK/UNIQUE 제약, 잠금, 인덱스, 마이그레이션 기능 모두 없음
- 각 패키지 관리자가 자체 검증·색인 시스템을 구축해야 함
결론
- Git은 소스 코드 협업에는 탁월하지만, 패키지 메타데이터 질의나 대규모 레지스트리 관리에는 부적합
- 대부분의 패키지 관리자는 결국 HTTP 기반 인덱스나 데이터베이스로 전환
- Git의 장점(버전 이력, PR 워크플로)은 매력적이지만, 데이터베이스 대체로는 실패
- 새로운 패키지 관리자를 설계할 때 Git 인덱스가 매력적으로 보이더라도, Cargo·Homebrew·CocoaPods·vcpkg·Go의 사례처럼 동일한 한계에 도달함
기여자들의 기여를 받기위해 시스템을 별도로 만들기보단 git이 간편하니까 쓰는거죠. 한계라고 하는데 별로 공감은 안되고, 현실적인 문제에 대한 대안은 전혀 안보이네요.
Hacker News 의견들
-
이건 일종의 공유지의 비극처럼 보임. GitHub은 무료이고 훌륭한 기능이 많으니 다들 쓰려 함. 하지만 이런 의사결정은 외부효과가 있을 때 항상 발생함
내가 가장 중요하게 생각하는 외부효과는 사용자 시간임. 대부분의 소프트웨어 회사는 엔지니어링 시간의 비용만 신경 쓰고, 사용자 시간을 무시함. 기능 개발에는 열중하지만 사용자 상호작용 시간은 최적화하지 않음. 예를 들어, 내가 앱을 1초 빠르게 만드는 데 1시간을 쓴다면, 백만 명의 사용자가 매년 277시간을 절약함. 하지만 사용자 시간은 외부효과라서 이런 최적화는 거의 이루어지지 않음
결국 사용자는 쓸데없이 데이터를 더 다운로드하고 기다리게 되며, 개발자는 그 낭비에 책임을 지지 않음- “소프트웨어 하우스”라는 표현이 정확히 뭘 의미하는지 모르겠지만, 내가 일한 대부분의 소비자용 소프트웨어 제품은 시작 속도나 지연 시간 같은 지표를 핵심적으로 추적했음. 이런 건 수십 년 전부터 상식이었음. 예를 들어, Amazon이 페이지 로딩 몇 밀리초 차이로 수백만 달러를 잃는다는 얘기도 자주 들었음
- 이건 “속도도 하나의 기능(feature) ”이라는 말과 같은 맥락임. 다만 사용자 시간은 단순한 성능뿐 아니라 UI 설계에도 크게 좌우됨
- “공유지의 비극”은 아니라고 생각함. GitHub은 Microsoft의 소유이므로, 그들이 감당할 수 있다고 판단한 것임. 진짜 공유지는 아무도 소유하지 않고 모두가 혜택을 보는 구조여야 함
- 이 문제를 깊이 생각하다 보면 Alan Kay의 말이 떠오름 — “진짜로 소프트웨어를 중요하게 여긴다면 하드웨어도 직접 만들어야 한다”. 네트워크를 통한 로딩은 본질적으로 나쁜 사용자 경험임. 진정으로 사용자를 존중하려면 로컬 우선(native-first) 애플리케이션을 만들어야 함. 하지만 이렇게까지 사용자 경험을 존중하는 회사는 극히 드묾
- Andy Hertzfeld의 글 “Saving Lives”을 보면 흥미로움 — “Macintosh 부팅이 너무 느리다. 더 빠르게 만들어야 한다!”는 일화가 있음
-
나는 C용 Cargo/UV를 만들고 있음. 좋은 글이라 깊이 공감함.
처음 시작할 때 레지스트리 운영은 정말 어려운 일임. 코드 작성과 도구 품질 확보, 커뮤니티 확산뿐 아니라 전 세계 트래픽을 감당할 인프라까지 고민해야 함. 이런 상황에서 git 기반 솔루션은 매력적임
하지만 문제는 sparse checkout임. git으로 패키지 매니페스트를 버전 관리하고 싶지만, 임의의 커밋을 추적해야 해서 비효율적임. 결국 두 번의 커밋을 푸시해야 하는 구조라 현실적으로 불가능함
Conan의 접근이 가장 실용적이라 생각함. 완벽한 재현성 대신 조건부 로직을 매니페스트에 넣는 방식임. 버전 범위별 매니페스트 매핑도 가능함. 완벽하진 않지만 실용적이고 유용한 절충안임.
물론 진짜 해결책은 데이터베이스를 쓰는 것이지만, 누가 서버비와 유지비를 대신 내줄 것도 아니니 현실적으로 어렵음- 관점을 바꿔보면, 성공한 패키지 매니저 대부분이 처음엔 Git 기반으로 시작했다가 필요할 때 더 효율적인 구조로 옮겨갔음
- Arch Linux AUR 방식도 고려할 만함. 각 패키지가 독립된 git 저장소를 갖고, 매니페스트만 포함함. 이렇게 하면 monorepo 문제나 참조 악몽을 피할 수 있음
- S3 같은 단순한 HTTP 백엔드로 저장소를 운영하는 것도 매력적임. 초기에 단일 서버로 시작하고, 인기가 생기면 스폰서를 찾아 클라우드로 옮기면 됨.
돈과 독립성이 문제라면 P2P 방식도 가능함. 다만 CI 캐싱이 안 되면 트래픽이 폭증할 수 있음 - 아직 사용자가 많지 않다면 전 세계를 대상으로 한 인프라를 미리 준비하는 건 시기상조임
- 굳이 모든 히스토리 데이터를 사용자에게 보여줄 필요는 없음. post-commit hook으로 HEAD 상태만 정적 파일로 렌더링해 GitHub Pages처럼 제공하면 됨.
Debian, Fedora, openSUSE 같은 리눅스 배포판의 미러 구조도 참고할 만함
-
이 글은 두 가지 문제를 혼동하고 있음. 하나는 git을 패키지 인덱스 데이터베이스로 쓰는 것, 다른 하나는 각 패키지 코드를 git으로 가져오는 것임. 둘은 별개임.
인덱스는 git으로, 패키지는 zip/tar로 할 수도 있고, 반대도 가능함. Go의 경우는 아예 인덱스가 없는 구조임- 글쓴이가 약간 혼란스러워 보임. “모든 사용자가 데이터베이스를 복제하지 말라”는 주장에는 동의하지만, 그렇다고 git 그래프를 데이터 인코딩에 못 쓸 이유는 없음.
GitHub의 백엔드 구현이나 2만 개 포크 같은 얘기는 본질과 무관함. git 워킹트리 없이도 효율적인 key-value 조회가 가능함.
“git 히스토리 리라이트가 DB 마이그레이션과 같다”는 주장도 이상함. 차라리 Postgres 하나 돌리는 게 낫지 않겠음? - 글의 요지는 코드 자체가 아니라 go.mod 파일을 가져오는 과정에 있음. 그래서 해결책으로 go.mod를 별도로 호스팅한 것임
- git에서도 필요한 단일 파일만 가져오기는 가능하지만, 여전히 구조적으로 어색함
- 글쓴이가 약간 혼란스러워 보임. “모든 사용자가 데이터베이스를 복제하지 말라”는 주장에는 동의하지만, 그렇다고 git 그래프를 데이터 인코딩에 못 쓸 이유는 없음.
-
“작동할 때는 쉬운 방법을 쓰고, 안 되면 고치자”는 접근은 현실적임.
Julia도 같은 방식을 쓰고 있고, Rust보다 패키지가 1/7 수준이라 아직 문제 없음.
최상위 Registry.toml 파일만 받아 필요한 패키지만 다운로드하도록 개선할 수 있음. 큰 문제는 아님- Julia는 git 레지스트리를 공식 원장(ledger) 으로만 쓰고, 실제 클라이언트는 Pkg Protocol을 사용함
- 이 접근을 “FAFO(해보고 망해보자) ” 정신이라 부를 수도 있음. 실용적이긴 하지만 개인적으로는 선호하지 않음
- 이런 태도는 비윤리적이라 생각함. “일단 쉽게 만들고 나중에 고치자”는 사고방식은 결국 기술 부채를 키움.
“Move fast and break things” 문화가 낳은 결과가 지금의 느리고 버그 많은 소프트웨어임 - 문제를 나중에 고치면 비용이 기하급수적으로 증가함. 결국 “조금 고장난 채로 그냥 쓰자”는 결론이 됨. 글에서도 vcpkg 사례가 그 예임
- 누군가 UUID를 의도적으로 조작한 것처럼 보이는 예시가 있는데, 그게 가능한 게 좀 걱정스러움
-
“Git은 패키지 매니저의 시작점으로 훌륭한 데이터베이스”라는 결론에 동의함
- 다만 클라이언트는 전체 저장소를 받지 않고 캐시나 DB 계층을 두는 게 좋음. CI/CD 환경에서는 특히 효율성이 중요함
- Nixpkgs도 Git 덕분에 성공했음. 스케일 문제는 나중의 사치스러운 고민임
- 하지만 Git을 찬양하기 전에 데이터베이스 연구를 조금이라도 찾아보는 게 좋음
- Git은 공급망 악몽을 초래할 수도 있음. Leftpad 사태가 매주 반복될 수도 있음
- Git은 패키지 매니저용 DB로는 형편없음. 다만 GitHub이 무료로 호스팅해주니 다들 쓰는 것뿐임
-
“결국 잘 됐잖아”라는 입장임. 초기 운영에는 충분히 도움이 됐고, 스케일 문제는 나중에 해결 가능했음
- 하지만 일부 프로젝트는 아키텍처적 한계 때문에 git에서 벗어나지 못하고 있음
- git처럼 파일 시스템 기반 스토어로 시작하면 나중에 프로토콜 전환이 거의 불가능함. 처음부터 API 중심 설계로 가야 함
- 오히려 git을 더 효율적으로 활용할 기회가 있음. 대안을 제시하지 않고 git을 버리자는 건 반쪽짜리 결론임
- “0에서 1조 사용자까지 스케일 안 됐다고 쓰레기라니”라는 반어적 농담도 있음
-
여기엔 생존자 편향(survivorship bias) 이 있음. Cargo가 성공했기에 git 인덱스가 커져 문제가 된 것임.
대부분의 작은 프로젝트는 git을 데이터 배포 프로토콜로 잘 쓰고 있음.
스케일이 불확실한 초기에는 git과 GitHub을 활용해 핵심 문제에 집중하는 게 합리적임- 너무 이른 최적화를 경계해야 함. Cargo나 Homebrew도 쉬운 길을 택해 성장했고, 스케일 문제는 나중의 “좋은 문제”였음
-
HN 첫 페이지에서 “지금 네가 하는 게 잘못됐다”는 글을 보면 늘 겸손해짐.
나도 몇 번 그런 경험이 있음. 이번엔 PG Notify 관련 글이 그랬음.
하지만 지금은 혼자 개발 중이고, 프로젝트가 성공할지조차 모르니 git으로 플러그인 배포하는 게 가장 현실적임.
그래도 나중에 스케일 문제가 생기면 이 글을 참고할 예정임- 지금도 몇 가지 함정은 피할 수 있음. GitHub 의존성 같은 벤더 락인이 더 큰 문제일 수도 있음
-
나는 개인적으로 Forgejo로 코드를 호스팅함. 외부 노출 없이 mTLS로 보호함.
하지만 Go 모듈은 인증서를 요구해서 내 Forgejo 인스턴스를 인식하지 못함.
SSH를 써도 HTTPS 접근이 필요하다고 해서 결국 replace directive로 로컬 복제본을 사용함. 꽤 번거로움- 모듈 경로 끝에
.git을 붙이고$GOPRIVATE를 설정하면 HTTPS 요청 없이 git 명령어 인증을 쓸 수 있음. 공식 문서 참고 - 인스턴스의 TLS 인증서(CA) 를 신뢰 저장소에 추가하면 HTTPS 다운로드도 가능함
- “HTTP 접근이 필요하다”는 건 사실이 아님. 로컬 프록시로 해결 가능함
- Tailscale DNS와 인증서를 쓰면 외부 노출 없이 Let’s Encrypt 인증서를 받을 수 있음
- 모듈 경로 끝에
-
패키지 매니저뿐 아니라 많은 작은 프로젝트가 데이터를 git 저장소에 크라우드소싱함.
대부분은 규모가 작아 기술적 한계에 부딪히지 않음.
다만 이런 구조는 비개발자 참여 장벽을 높임. 패키지 매니저는 예외지만 일반 프로젝트엔 문제임
나는 이런 문제를 돕기 위해 Datatig이라는 오픈소스 라이브러리를 만들었음.
관련 발표 자료는 여기에 있음. 앞으로 이 글을 참고해 스케일링 관련 내용도 추가할 예정임