Published on

대형 모노레포에서 Git LFS가 느려질 때 진단법

Authors

서로 다른 팀이 하나의 대형 모노레포를 공유하는 환경에서는 Git LFS가 평소엔 “그럭저럭” 돌아가다가도, 어느 날부터 clone/pull/checkout/lfs fetch가 갑자기 몇 배 이상 느려지는 일이 흔합니다. 문제는 원인이 하나가 아니라는 점입니다. 네트워크, 인증, LFS 서버, Git 훅, 워킹트리 필터, 락 경합, 로컬 캐시/디스크, CI 동시성 등 여러 층에서 지연이 발생할 수 있습니다.

이 글은 “무엇이 느린지”를 먼저 분해하고, 가장 비용이 큰 구간부터 빠르게 배제해 나가는 진단 루틴을 제공합니다. 목표는 감으로 설정을 건드리는 것이 아니라, **증거(로그/지표/재현 커맨드)**로 병목을 특정하는 것입니다.

문제 접근 방식은 다른 장애 진단과 동일합니다. 예를 들어 MySQL Replication Lag 폭증 원인·튜닝 7단계처럼 “계층을 나누고, 측정하고, 한 번에 하나씩 바꾸는” 방식이 Git LFS에도 그대로 적용됩니다.

1) 먼저 ‘어느 단계’가 느린지 분해하기

Git LFS가 느리다고 느끼는 구간은 보통 아래 중 하나입니다.

  • 전송(네트워크/서버): git lfs fetch, git lfs pull에서 다운로드가 느림
  • 체크아웃(필터/파일 I/O): git checkout, git switch, git reset --hard에서 워킹트리 반영이 느림
  • 상태 확인(스캔): git status, git diff가 느려짐(대형 워킹트리 + LFS 포인터/스머지 영향)
  • 락/대기: “멈춘 것처럼” 보이며 CPU도 낮고 진행이 안 됨(다른 Git/LFS 프로세스와 경합)

가장 먼저 할 일은, 동일한 리포지토리에서 아래처럼 명시적으로 LFS 단계만 분리 실행해 시간을 재는 것입니다.

# 1) LFS 객체 다운로드만
GIT_TRACE=1 GIT_CURL_VERBOSE=1 git lfs fetch --all

# 2) 다운로드된 LFS를 워킹트리에 반영(체크아웃 단계)
GIT_TRACE=1 git lfs checkout

# 3) 일반 Git 오브젝트만 받고 LFS는 건너뛰기(비교용)
GIT_LFS_SKIP_SMUDGE=1 git clone <repo-url> repo-no-lfs
cd repo-no-lfs

time git checkout <branch>
  • fetch가 느리면 네트워크/서버/인증/대용량 객체/병렬성 문제 가능성이 큽니다.
  • checkout이 느리면 로컬 디스크, 안티바이러스, 워킹트리 파일 수, 필터 체인, 파일 잠금 문제가 자주 원인입니다.
  • GIT_LFS_SKIP_SMUDGE=1로 “Git 자체는 빠른데 LFS만 느린지”를 즉시 분리할 수 있습니다.

2) 로그를 ‘제대로’ 켜서 어디서 기다리는지 확인하기

LFS는 내부적으로 HTTP 요청, 인증, 리다이렉트, 프록시, TLS 핸드셰이크, 재시도 등을 수행합니다. 눈에 보이는 진행률만으로는 원인을 알기 어렵습니다.

필수 디버그 환경변수

# Git 레벨 트레이스
export GIT_TRACE=1
export GIT_TRACE_PACKET=1
export GIT_TRACE_PERFORMANCE=1

# HTTP 상세(주의: 토큰/쿠키가 로그에 노출될 수 있음)
export GIT_CURL_VERBOSE=1

# LFS 자체 로그
export GIT_TRACE=1
export GIT_LFS_TRACE=1
export GIT_LFS_TRACE_PERFORMANCE=1

git lfs fetch

로그에서 특히 봐야 할 패턴은 다음입니다.

  • 동일 URL로 재시도가 반복됨(네트워크 불안, 429/5xx, 프록시 문제)
  • Authorization 갱신/토큰 재발급이 과도함(SSO/자격증명 헬퍼 지연)
  • 302 리다이렉트 체인이 길어짐(프론트 도메인 변경, CDN 설정)
  • 다운로드는 빠른데 마지막에 checkout에서 오래 걸림(파일 I/O 병목)

3) 가장 흔한 원인 1: 자격증명/SSO/프록시 지연

대형 조직에서는 LFS가 Git과 다른 도메인/엔드포인트를 쓰거나, SSO 토큰을 별도로 발급받는 경우가 있습니다. 이때 인증 갱신이 병목이 되면, 작은 객체 여러 개를 받을수록 느려집니다.

진단 체크

# 현재 LFS 엔드포인트 확인
git lfs env

# 리모트 URL 및 LFS URL이 다르면(특히 다른 도메인) 인증 흐름 확인 필요
git remote -v

# 프록시 환경 변수 확인
env | grep -i proxy
  • git lfs env에서 Endpoint=가 예상과 다르거나, 사내 프록시를 타고 있으면 지연이 급증할 수 있습니다.
  • 프록시가 TLS 검사(미들박스)를 하면 대용량 전송에서 재전송/세션 끊김이 발생하기도 합니다.

빠른 우회 테스트

프록시가 의심되면, 동일 네트워크에서 프록시를 끄고(가능한 범위에서) 단발성으로 속도 비교를 해보세요.

# 예: 프록시를 비활성화한 세션에서만 테스트
unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy

time git lfs fetch

4) 가장 흔한 원인 2: LFS 객체 수 폭증(작은 파일 다량)

Git LFS는 “큰 바이너리 몇 개”에 최적화된 경우가 많습니다. 그런데 모노레포에서는 모델/에셋/테스트 데이터가 늘면서 수천~수만 개의 LFS 객체가 생기기 쉽습니다. 이 경우 “대역폭”보다 “요청 수/메타데이터 처리”가 병목이 됩니다.

어떤 파일이 LFS에 잡히는지 확인

# LFS 추적 패턴 확인
cat .gitattributes

# LFS 파일 목록(현재 체크아웃 기준)
git lfs ls-files | head

# LFS 오브젝트 상태 요약
git lfs status

특히 .gitattributes에 너무 광범위한 패턴(예: *.zip, *.bin 등)이 들어가면, 의도치 않게 작은 파일까지 LFS로 빨려 들어가 성능이 급격히 나빠집니다.

대형 파일 상위 랭킹(리포지토리 관점)

# LFS 오브젝트 크기 상위 확인
git lfs ls-files --size | sort -k2 -h | tail -n 20

여기서 “큰 파일 몇 개”가 원인인지, “중소형 파일이 너무 많음”이 원인인지 감을 잡을 수 있습니다.

5) 가장 흔한 원인 3: 체크아웃 단계의 디스크 I/O 병목

git lfs fetch는 빨리 끝나는데 git checkout이나 git lfs checkout이 오래 걸리면, 대부분 로컬 환경 요인입니다.

  • 느린 HDD/네트워크 드라이브에 워킹트리가 있음
  • 백신/EDR이 새로 생성되는 바이너리 파일을 실시간 스캔
  • 파일 잠금(Windows에서 흔함)
  • 워킹트리 파일 수가 너무 많아 메타데이터 작업이 과도

체크아웃만 분리해 시간 측정

# 이미 fetch가 끝났다는 가정

time git lfs checkout

Windows에서 특히 확인할 것

  • 워킹 디렉터리가 OneDrive 동기화 폴더, 네트워크 홈 디렉터리인지
  • 보안 제품 예외(리포지토리 경로 예외) 적용 가능 여부

이 문제는 “LFS 설정”을 바꿔도 해결이 안 되는 경우가 많고, 로컬 디스크/보안 정책이 병목인 경우가 많습니다.

6) 락 경합/좀비 프로세스: 멈춘 것처럼 보일 때

LFS는 로컬 캐시와 객체 스토어를 다루면서 락을 잡습니다. CI나 개발 머신에서 여러 Git 작업이 동시에 돌면 락 대기가 길어질 수 있습니다.

진단

# 실행 중인 git/lfs 관련 프로세스 확인(리눅스/맥)
ps aux | grep -E "git|git-lfs" | grep -v grep

# LFS 로그 파일 위치 확인
git lfs env | grep -i log
  • 동일 리포지토리에서 동시에 checkout/pull/lfs fetch가 돌면 경합이 생길 수 있습니다.
  • Jenkins 에이전트에서 병렬 빌드가 같은 워크스페이스를 공유하면 특히 잘 터집니다. 이 경우 워크스페이스를 분리하거나 동시성 제한이 필요합니다. 관련해서는 Jenkins 빌드가 멈출 때 - 에이전트 오프라인 진단처럼 “작업 단위 격리”가 효과적입니다.

7) 서버 측 병목: LFS 서버, CDN, 레이트 리밋

조직이 GitHub Enterprise, GitLab, Bitbucket, 사내 LFS 서버를 쓰는 경우, 어느 순간부터 느려졌다면 서버 측 변경(CDN 설정, 스토리지 변경, 레이트 리밋, WAF/보안 정책)이 원인일 수 있습니다.

클라이언트에서 확인 가능한 신호

GIT_CURL_VERBOSE=1 로그에서 아래를 찾습니다.

  • HTTP 상태코드 429(rate limit) 또는 503/504
  • 특정 시간대에만 급격히 느려짐(피크 트래픽)
  • Content-Length 대비 전송 속도 급락

또한 LFS는 “대용량 단일 파일”보다 “다수 파일”에서 서버의 동시 처리/DB 성능 영향을 크게 받습니다.

빠른 검증: 동시 다운로드 수 조정

환경에 따라 LFS 동시 다운로드가 너무 높으면 서버/프록시가 버티지 못해 재시도가 늘고, 오히려 느려질 수 있습니다.

# 현재 설정 확인
git config --global --get lfs.concurrenttransfers

# 예: 동시 전송 수를 낮춰 재시도 감소를 유도
git config --global lfs.concurrenttransfers 3

# 다시 측정

time git lfs fetch

반대로 네트워크가 충분하고 서버가 안정적이라면 늘려서 개선되는 경우도 있습니다. 중요한 건 “바꾸기 전/후 로그와 시간”을 남기는 것입니다.

8) 로컬 LFS 캐시/스토리지 문제: 디스크 부족, 파일시스템 이슈

LFS는 기본적으로 로컬에 객체를 보관합니다. 디스크가 부족하거나 파일시스템에 문제가 있으면, 다운로드/체크아웃이 비정상적으로 느려질 수 있습니다.

캐시 위치 및 용량 확인

git lfs env | grep -i "LocalStorageDir\|TempDir"

df -h .

안전한 정리 루틴(주의: 재다운로드 발생)

# 사용되지 않는 LFS 오브젝트 정리
# (공유 캐시를 쓰는 환경이면 정책 확인 후 수행)
git lfs prune
  • prune 후에는 필요한 객체를 다시 받아야 하므로, 네트워크가 느린 환경에서는 오히려 비용이 커질 수 있습니다.
  • 하지만 “디스크 압박으로 인한 스왑/파일시스템 지연”이 원인이라면 즉효가 나기도 합니다.

9) 모노레포 운영 팁: 느려지기 전에 구조적으로 줄이는 방법

진단으로 원인을 찾았다면, 재발 방지 차원에서 아래를 고려할 만합니다.

  • LFS 추적 범위를 좁히기: .gitattributes에서 광범위 패턴을 줄이고, 진짜 대형 바이너리만 LFS로
  • 대형 테스트 데이터/모델 분리: 리포지토리 외부 아티팩트 스토어(S3, Artifactory 등)로 이동
  • CI 워크스페이스 격리: 병렬 작업이 같은 디렉터리를 공유하지 않게 구성
  • 부분 체크아웃/스파스 체크아웃: 모노레포에서 필요한 경로만 받도록 최적화

특히 “필요한 폴더만 받기”는 네트워크뿐 아니라 체크아웃 파일 수를 줄여 체감 성능이 크게 개선됩니다.

# 스파스 체크아웃 예시

git clone --filter=blob:none <repo-url> repo
cd repo

git sparse-checkout init --cone
# 필요한 경로만 지정
git sparse-checkout set apps/web packages/shared

# LFS는 필요한 시점에만
GIT_LFS_SKIP_SMUDGE=1 git checkout main

git lfs pull

위에서 --filter=blob:none 같은 옵션은 LFS 자체를 대체하진 않지만, “불필요한 Git blob 다운로드”를 줄여 전체 체감 속도를 개선하는 데 도움이 됩니다.

10) 현장에서 바로 쓰는 체크리스트(요약)

A. 전송이 느리다(git lfs fetch)

  1. GIT_CURL_VERBOSE=1로 HTTP 상태코드/재시도/리다이렉트 확인
  2. git lfs env로 Endpoint/프록시/인증 흐름 확인
  3. lfs.concurrenttransfers를 3~8 범위에서 조정하며 재시도 여부 비교
  4. 특정 시간대만 느리면 서버/CDN/레이트 리밋 가능성

B. 체크아웃이 느리다(git lfs checkout, git checkout)

  1. 백신/EDR/동기화 폴더/네트워크 드라이브 여부 확인
  2. GIT_LFS_SKIP_SMUDGE=1로 Git vs LFS 분리
  3. 워킹트리 파일 수를 스파스 체크아웃으로 줄여 비교

C. 멈춘 것처럼 보인다

  1. ps로 중복 Git/LFS 프로세스 확인
  2. CI 동시성/워크스페이스 공유 여부 확인

대형 모노레포에서 Git LFS 성능 문제는 “한 번에 한 가지 원인”인 경우가 드뭅니다. 그래서 더더욱 단계를 분해하고, 로그를 켜고, 전송/체크아웃/락을 분리해야 합니다. 이 루틴을 팀의 런북으로 만들어두면, 어느 날 갑자기 느려졌을 때도 30분 안에 원인 범위를 크게 좁힐 수 있습니다.

추가로, 리포지토리 규모가 커질수록 “원인 분리”가 중요해집니다. 애플리케이션 성능 진단에서 RSC 번들 폭증을 계층적으로 쪼개듯이(Next.js 14 RSC로 번들 커질 때 6가지 해결법), Git LFS도 네트워크/서버/로컬 I/O/동시성으로 나눠 측정하면 답이 보입니다.