Git 서브모듈 vs 서브트리: 무엇을 선택할까
git submodule과 git subtree의 저장 방식, clone 방법, 업스트림 기여, 팀 학습 비용을 비교하고 상황에 맞는 선택 기준을 정리한다.
지난 글에서 서브모듈의 함정들을 살펴봤다. 이번에는 시야를 넓혀 서브모듈과 서브트리를 비교하고 어떤 상황에서 무엇을 선택해야 하는지 기준을 잡아본다.
두 방법의 핵심 차이
서브모듈과 서브트리는 “외부 저장소 코드를 메인 저장소에 포함하는” 같은 목표를 추구하지만 접근 방식이 근본적으로 다르다.
git submodule: 외부 저장소의 특정 커밋에 대한 포인터만 저장한다. 실제 파일은 외부 저장소에 있고, 팀원이 저장소를 받을 때 별도로 초기화해야 한다.
git subtree: 외부 저장소의 파일을 직접 메인 저장소 히스토리에 병합한다. 팀원은 일반 git clone만으로 모든 파일을 받는다.
세부 비교
clone 경험
서브트리가 명확히 유리하다. 서브모듈이 포함된 저장소는 클론 후 git submodule update --init --recursive를 실행하지 않으면 파일이 없는 상태다. 새 팀원 온보딩 시 이 단계를 빠뜨리는 일이 자주 발생한다.
# 서브모듈 저장소 클론
git clone --recurse-submodules https://github.com/org/main.git
# 서브트리 저장소 클론 (추가 단계 불필요)
git clone https://github.com/org/main.git
업스트림에 기여하기
서브모듈이 유리하다. 서브모듈은 독립 저장소이기 때문에 서브모듈 디렉터리 안에서 직접 브랜치를 만들고 커밋 후 푸시할 수 있다.
서브트리에서 업스트림에 변경을 반영하려면 git subtree push를 사용해야 한다. 이 명령이 메인 저장소 히스토리에서 해당 경로와 관련된 커밋만 필터링해 업스트림으로 푸시하는데, 대규모 히스토리에서는 시간이 오래 걸린다.
# 서브트리로 업스트림에 기여
git subtree push --prefix=libs/util origin-util main
버전 핀 관리
서브모듈은 특정 커밋 SHA를 명시적으로 추적하므로 버전 핀 관리가 명확하다. “서브모듈이 정확히 어떤 버전을 사용하는지”를 코드 리뷰 시 쉽게 확인할 수 있다.
서브트리는 파일이 통합되어 있어 “어느 버전에서 가져왔는지”를 커밋 메시지로만 추적한다.
저장소 크기
서브모듈은 포인터만 저장하므로 메인 저장소 크기에 영향을 주지 않는다. 서브트리는 외부 저장소의 전체 히스토리가 메인 저장소에 병합되어 저장소 크기가 증가한다.
선택 가이드
서브모듈을 선택해야 할 때:
- 외부 저장소 버전을 엄격하게 핀 관리해야 할 때
- 서브모듈에 직접 기여하고 변경을 업스트림에 반영해야 할 때
- 팀이 서브모듈 워크플로에 이미 익숙할 때
서브트리를 선택해야 할 때:
- 팀 전체가 서브모듈 워크플로를 처음 접할 때
- 외부 코드를 “그냥 포함”하고 업스트림 기여는 거의 없을 때
git clone한 번으로 모든 것이 준비되는 단순함이 중요할 때
둘 다 고려하기 전에:
- npm, pip, Maven 같은 패키지 매니저로 관리할 수 있다면 그것이 먼저다
- 내부 코드라면 모노레포 구조(Turborepo, Nx)를 검토하라
실무에서의 현실
실제 프로젝트를 보면 서브모듈보다 서브트리를 선택하는 팀이 더 많다. 서브모듈의 Detached HEAD 함정, 빈 디렉터리 문제 등이 팀 생산성을 저하시키기 때문이다. 반면 서브트리는 일반 Git 워크플로와 동일해 러닝커브가 낮다.
다만 서브트리도 “히스토리가 섞인다”, “subtree push가 느리다” 같은 단점이 있어 완벽한 솔루션은 아니다. 상황에 맞게 선택하는 것이 중요하다.
지난 글: 서브모듈 주의사항과 함정
다음 글: Git 서브트리 기초
읽어주셔서 감사합니다. 😊