codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›tools

Git Submodule · Subtree · LFS — 저장소 안에 다른 저장소

2026-04-28 게시· 2026-05-18 갱신·0회 조회

Git Submodule · Subtree · LFS — 저장소 안에 다른 저장소

한 저장소가 다른 저장소의 코드를 함께 가져가야 할 때, 또는 큰 바이너리 파일을 저장소에 넣어야 할 때 떠오르는 옵션. Submodule · Subtree · Sparse Checkout · Git LFS 가 그것. 각자 강점이 다르고 함정도 다릅니다. 이 글은 네 도구의 출자 · 동작 · 자주 만나는 함정 · 모노레포에서 submodule 을 자주 피하는 이유.

1. 네 도구에 대한 이야기

도구 출자 해결하려는 문제
Git Submodule Git 1.5.3 (2007) 외부 저장소를 특정 커밋으로 고정해 포함
Git Subtree git-subtree 가 2009 년 contrib 합류 외부 저장소를 한 디렉터리로 통합. 부모 히스토리에 병합
Sparse Checkout Git 1.7 (2010), v2.25 에서 큰 개선 큰 저장소에서 일부 디렉터리만 체크아웃
Git LFS GitHub, 2015 큰 바이너리 파일을 별도 저장소에

각각이 풀려는 문제가 다릅니다. 같은 자리에 모두 적용 가능한 도구는 거의 없음.

2. Submodule

부모 저장소 안에 자식 저장소의 참조 만 둠. 자식의 실제 파일은 자기 저장소에 있고, 부모는 "이 디렉터리는 자식 저장소의 SHA xxx 커밋이다" 라는 메타정보만 들고 있음.

# 추가
git submodule add https://github.com/foo/lib.git vendor/lib
# .gitmodules 파일이 생기고 vendor/lib 에 자식 저장소 클론

# 클론 시 함께 받기
git clone --recurse-submodules https://github.com/me/parent.git

# 이미 클론한 저장소에 submodule 받기
git submodule update --init --recursive

# 자식 갱신
git submodule update --remote vendor/lib
git add vendor/lib && git commit -m "bump lib"

.gitmodules 파일이 SSOT:

[submodule "vendor/lib"]
    path = vendor/lib
    url = https://github.com/foo/lib.git
    branch = main
강점 약점
자식 저장소의 히스토리가 분리 클론 · 체크아웃 절차가 복잡
정확한 커밋 핀고정 새 작업자가 자주 헷갈림
권한 분리 (private / public 혼합) CI 설정 추가 필요

3. Subtree

자식 저장소의 내용을 부모의 한 디렉터리로 물리적으로 합침. 부모 히스토리에 자식 커밋이 들어감.

# 추가
git subtree add --prefix=vendor/lib https://github.com/foo/lib.git main --squash

# 자식의 변경을 가져오기
git subtree pull --prefix=vendor/lib https://github.com/foo/lib.git main --squash

# 부모에서 한 변경을 자식에게
git subtree push --prefix=vendor/lib origin-lib main
강점 약점
클론한 사람이 추가 명령 없이 모든 코드 보유 부모 히스토리가 비대해짐
평범한 클론 · 풀이 그대로 동작 자식으로 변경을 보내는 절차가 헷갈림
.gitmodules 같은 별도 메타 없음 자식의 히스토리가 부모에 섞임

4. Sparse Checkout

큰 모노레포에서 일부 디렉터리만 받음. Git v2.25 의 git sparse-checkout 명령이 큰 개선:

git clone --filter=blob:none --no-checkout https://github.com/big/mono.git
cd mono
git sparse-checkout init --cone
git sparse-checkout set apps/web packages/ui
git checkout main

--cone 모드는 디렉터리 단위로만 가능하지만 빠르고 안전. 옛 non-cone 모드는 glob 패턴이지만 성능 저하 · 불안정.

서브모듈처럼 외부 저장소 포함이 아니라 같은 저장소에서 일부만 받는 도구.

5. Git LFS

큰 바이너리 (이미지 · 동영상 · 머신러닝 가중치) 가 매 커밋마다 압축 · 전송되면 저장소가 곧 GB 급으로 부풂. LFS 는 그 파일들을 별도 LFS 서버에 두고, 저장소에는 작은 포인터 파일만:

# 한 번 설치
git lfs install

# 추적 추가
git lfs track "*.psd"
git lfs track "models/*.safetensors"
# .gitattributes 가 갱신됨
git add .gitattributes

# 평소처럼 add · commit · push
git add design.psd
git commit -m "add design"
git push

.gitattributes 가 SSOT:

*.psd filter=lfs diff=lfs merge=lfs -text
models/*.safetensors filter=lfs diff=lfs merge=lfs -text

GitHub 는 무료 1 GB 저장 + 1 GB / 월 대역폭. 더 필요하면 데이터팩 구매.

6. 한눈에 비교

시나리오 추천
외부 라이브러리를 정확한 커밋으로 고정 Submodule (또는 패키지 매니저로 충분하면 그것)
외부 코드를 자기 저장소에 합쳐 fork 처럼 관리 Subtree
한 거대한 모노레포의 일부만 받기 Sparse checkout
이미지 · 동영상 · 모델 가중치 등 큰 바이너리 LFS

7. 다른 길

위 도구 외:

  • 패키지 매니저 — 의존성 관리는 npm / pnpm / Cargo / Maven 같은 패키지 매니저가 더 자연스러운 자리가 많음. submodule 이 답인지 의심.
  • 모노레포 도구 — Nx · Turborepo · Bazel · Buck. 한 저장소 안에 여러 패키지를 두되 빌드 · 캐시 관리.
  • vendoring — 외부 코드를 통째로 복사해 자기 코드처럼. 자식 갱신을 손으로 추적해야 함.
  • Workspaces — pnpm / npm / Yarn workspaces. 같은 저장소 안의 여러 패키지 의존성 자동 링크.

8. 자주 걸리는 자리

Submodule

  • 새 작업자가 git clone 만 하고 끝남 — vendor/lib 가 빈 폴더. README 에 --recurse-submodules 또는 git submodule update --init 안내 필요.
  • 자식 변경 후 git push 만 하고 부모 갱신을 안 함 — 동료 머신에서 자식이 옛 SHA 로 보임. 자식에서 push → 부모에서 git add vendor/lib → 부모 push 가 한 묶음.
  • CI 의 submodule 받기 — GitHub Actions 의 actions/checkout 은 기본이 submodules: false. submodules: recursive 를 명시.
  • detached HEAD — submodule 디렉터리는 기본이 detached HEAD. 자식 작업 시 의도하고 들어가는 자리.

Subtree

  • 부모 히스토리 비대화 — --squash 없이 자주 풀하면 히스토리가 빠르게 커짐.
  • 자식으로 push 가 익숙해지기 까다로움 — 한 사람이 책임지는 편이 안전.

Sparse checkout

  • non-cone 모드의 함정 — 패턴이 의도와 어긋나 일부 파일만 표시되는 일. --cone 모드 권장.
  • CI 가 전체를 체크아웃 — 빌드는 로컬보다 CI 가 무거운 경우가 흔함.

LFS

  • 첫 추적 후 기존 큰 파일 — git lfs track 만으로는 과거 커밋의 큰 파일은 LFS 로 안 옮겨짐. git lfs migrate import 필요.
  • fork 시 LFS 객체 — 일부 호스팅은 fork 시 LFS 가 따라가지 않음.
  • 무료 한도 — GitHub 의 1 GB / 월 대역폭은 동영상 한 번이면 소진. 사전 계산.

9. 모노레포에서 submodule 을 자주 피하는 이유

같은 회사 · 팀이 만든 여러 패키지를 한 저장소에 두는 모노레포에서는 submodule 이 자주 마찰:

  • 한 PR 이 두 저장소 (부모 · 자식) 를 동시에 만져야 하는 절차 부담.
  • CI 캐시 · 의존성 그래프가 분산.
  • 새 사람의 진입 장벽 (clone, init, update 절차).

대신 패키지 매니저 워크스페이스 또는 Nx / Turborepo 가 자주 권장. submodule 은 외부 OSS 를 핀고정해 가져가는 자리, 또는 권한 · 라이선스가 분리되어야 하는 자리에 더 어울립니다.

하고픈 말

Submodule · Subtree · Sparse Checkout · LFS 넷은 같은 카테고리로 묶이지만 사실 각자 다른 문제를 풉니다. 모노레포라면 submodule 보다 워크스페이스 (pnpm · Yarn) · 모노레포 도구 (Turborepo · Nx) 가 마찰이 적은 자리. 큰 바이너리는 LFS 가 거의 표준. 도구를 고르기 전에 "이 자리에 진짜 필요한가" 부터 의심.

Next

  • (tools 끝)

git-submodule · Pro Git Submodules · git-subtree · git-sparse-checkout · Git LFS · GitHub LFS 한도 · Atlassian — Git Submodules vs Subtree · GitHub Blog — partial clone and shallow clone · Nx · Turborepo · Bazel · pnpm Workspaces 를 참고합니다.

tools 카테고리의 다른 글

카테고리 전체 보기 →
  • 정규표현식 — 패턴으로 문자열을 찾기
  • Python 의존성 도구의 역사
  • 린팅과 포매팅
  • 에디터 설정
  • Gradle
  • Git 워크플로