Git Submodule vs. Subtree

깃 (Git) 은 소프트웨어 개발에서 널리 사용되는 분산형 버전 관리 시스템으로, 복잡한 프로젝트를 효율적으로 관리할 수 있게 해준다. 대규모 프로젝트에서는 종종 여러 저장소 (repository) 에 분산된 코드를 하나의 프로젝트 내에서 통합해야 하는 필요성이 생긴다. 이러한 필요성을 해결하기 위해 깃은 두 가지 주요 접근 방식인 서브모듈 (Submodule) 과 서브트리 (Subtree) 를 제공한다.

Git SubmoduleGit Subtree 는 모두 하나의 Git 프로젝트 안에서 다른 Git 저장소를 하위 프로젝트처럼 관리하기 위한 방식이다. 하지만 방법은 다르다.

  • Submodule 은 다른 Git 저장소를 포인터로 연결하고 별도로 동기화해야 한다.
  • Subtree 는 다른 Git 저장소의 특정 디렉터리 구조를 **직접 병합 (merge)**하여 포함시키는 방식으로 외부 프로젝트의 전체 히스토리를 메인 저장소에 병합한다.

이 두 접근 방식은 서로 다른 작동 원리, 사용 사례, 장단점을 가지고 있으며, 개발 팀의 워크플로우와 프로젝트 요구사항에 따라 적합한 선택이 달라질 수 있다.

flowchart TD
    subgraph Submodule
        A1[Main Repo] --> B1[.gitmodules file]
        B1 --> C1[(Submodule Pointer)]
    end

    subgraph Subtree
        A2[Main Repo] --> B2[Subtree Merged Repo with History]
    end

    style Submodule fill:#f0f8ff,stroke:#333,stroke-width:1px
    style Subtree fill:#f8fff0,stroke:#333,stroke-width:1px

기본 개념

Git Submodule은 한 Git 저장소 내에 다른 독립적인 Git 저장소를 포함할 수 있게 해주는 기능으로, 부모 저장소에서 자식 저장소의 특정 커밋을 참조하는 방식으로 작동한다. 이는 의존성 버전을 고정하여 안정성을 확보할 수 있게 한다.

Git Subtree는 외부 저장소를 현재 프로젝트의 디렉토리로 직접 병합하여 관리하는 방식으로, 상위 저장소에 하위 저장소의 파일을 직접 추가하고 트래킹한다. 이를 통해 일반적인 Git 워크플로우로 모든 코드를 관리할 수 있다.

항목Git SubmoduleGit Subtree
버전 관리 방식하위 저장소의 특정 커밋을 참조 (포인터)하위 저장소의 전체 히스토리를 상위 저장소에 병합
독립적인 히스토리유지됨유지되지 않음 (상위 저장소에 합쳐짐)
외부 의존성실제 외부 저장소를 포함외부 저장소 내용을 복사하여 포함
업데이트 방식수동 업데이트 (submodule update, pull) 필요상위 저장소에서 pull --squash 또는 merge 가능
사용 복잡도비교적 복잡 (추적, 동기화 필요)단순 (일반 Git 명령으로 충분함)
사용 용도공통 라이브러리 공유, 버전 고정이 필요한 서드파티 코드 포함외부 저장소를 내부에서 관리하려는 경우, 일회성 통합 등에 적합

핵심 개념 및 목적 비교

구분SubmoduleSubtree
핵심 개념외부 저장소를 참조로 포함 (포인터 기반)외부 저장소의 내용을 디렉터리로 병합
파일 관리참조만 저장 (SHA 값)파일을 직접 추가하고 트래킹
메타데이터.gitmodules 파일 생성별도 메타데이터 파일 없음
목적서브 프로젝트 독립적 버전 관리전체 프로젝트와의 구조 통합 및 일관성
저장소 크기작음 (참조만 저장)큼 (전체 코드 포함)
독립성완전히 독립적인 저장소상위 저장소와 통합됨
협업외부 저장소 변경 시 수동 업데이트 필요메인 저장소 내에서 직접 수정 가능
클론 방식별도 초기화 필요 (init/update)자동으로 포함됨
히스토리 관리분리된 커밋 히스토리통합된 커밋 히스토리
사용하는 명령어git submodulegit subtree 또는 git merge, git remote
통합 방식commit ID 기준 참조내용 기반 병합 (history 유지 가능)
적합 사례컴포넌트 기반 개발 (CBD)시스템 기반 개발 (SBD)
병합 전략Fast-forward 불가능일반 병합 가능

시각적 비교 다이어그램

graph LR
    A[메인 저장소] --> B[Submodule: 링크 관리]
    A --> C[Subtree: 코드 복제]
    B --> D[외부 저장소 커밋 고정]
    C --> E[외부 코드 내재화]

구성 요소 및 원리

깃 서브모듈 (Git Submodule) 구성 요소 및 아키텍처 vs 깃 서브트리 (Git Subtree) 구성 요소 및 아키텍처

깃 서브모듈 (Git Submodule) 은 " 참조 기반 " 접근 방식을 사용한다. 메인 저장소는 서브모듈 저장소의 특정 커밋에 대한 참조 (포인터) 만 저장하고, 실제 코드는 서브모듈 저장소에서 관리된다. 이는 " 느슨한 결합 (loose coupling)" 원칙을 따르며, 각 모듈이 독립적으로 발전할 수 있게 한다. 메인 저장소에서 서브모듈을 추가하면, 메인 저장소는 서브모듈의 특정 커밋 해시를 참조한다. 이 참조 정보는 .gitmodules 파일과 깃 인덱스에 저장된다. 서브모듈의 내용이 변경되더라도, 메인 저장소의 참조는 명시적으로 업데이트하지 않는 한 변경되지 않는다.

깃 서브트리 (Git Subtree) 는 " 통합 기반 " 접근 방식을 사용한다. 외부 저장소의 코드를 메인 저장소에 복사하여 통합함으로써, 모든 코드와 히스토리가 단일 저장소 내에서 관리된다. 이는 " 강한 결합 (tight coupling)" 원칙을 따르며, 코드 베이스의 일관성을 유지한다. 서브트리를 추가할 때, 깃은 외부 저장소의 히스토리를 메인 저장소의 히스토리에 병합한다. 이는 특별한 병합 전략을 사용하여, 외부 저장소의 커밋을 메인 저장소의 특정 서브디렉토리에 적용한다. 이 과정에서 원본 저장소의 커밋 정보를 보존하여, 나중에 변경사항을 양방향으로 동기화할 수 있다.

  • Submodule 아키텍처:
1
2
3
4
Main Repo
├── .gitmodules
├── lib/
│   └── [Submodule] → 다른 Git Repo 연결 (포인터)
  • Subtree 아키텍처:
1
2
3
Main Repo
├── lib/
│   └── [Subtree 병합된 Repo] → Git history 포함됨

작동 원리

깃 서브모듈 (Git Submodule) 의 작동 원리

gitGraph
    commit id: "메인 저장소 초기화"
    branch submodule
    commit id: "서브모듈 개발"
    checkout main
    commit id: "서브모듈 추가"
    merge submodule tag: "참조 커밋 업데이트"
  1. 서브모듈 추가:

    1
    
    git submodule add <repository-url> <path>
    

    이 명령은 지정된 저장소를 서브모듈로 추가하고, .gitmodules 파일을 생성/업데이트한다.

  2. 저장소 클론 및 서브모듈 초기화:

    1
    2
    3
    
    git clone <main-repository-url>
    git submodule init
    git submodule update
    

    메인 저장소를 클론한 후, 서브모듈을 초기화하고 업데이트해야 서브모듈 코드를 가져온다.

  3. 서브모듈 업데이트:

    1
    2
    3
    4
    5
    
    cd <submodule-path>
    git pull origin master
    cd ..
    git add <submodule-path>
    git commit -m "Update submodule"
    

    서브모듈 코드를 최신 버전으로 업데이트하려면, 서브모듈 디렉토리에서 깃 명령을 실행한 후, 메인 저장소에서 변경된 참조를 커밋해야 한다.

깃 서브트리 (Git Subtree) 의 작동 원리

gitGraph
    commit id: "메인 저장소 초기화"
    commit id: "서브트리 코드 통합"
    commit id: "서브트리 코드 수정"
    commit id: "변경사항 병합"
  1. 서브트리 추가:

    1
    
    git subtree add --prefix=<path> <repository-url> <branch> --squash
    

    이 명령은 지정된 저장소의 코드를 메인 저장소의 지정된 경로에 복사하고, 원본 저장소의 히스토리를 메인 저장소에 병합한다.

  2. 저장소 클론:

    1
    
    git clone <main-repository-url>
    

    메인 저장소를 클론하면 서브트리 코드도 함께 포함되어 있어 추가 단계가 필요 없다.

  3. 서브트리 업데이트:

    1
    
    git subtree pull --prefix=<path> <repository-url> <branch> --squash
    

    서브트리 코드를 최신 버전으로 업데이트하려면, 이 명령을 사용하여 원본 저장소의 변경사항을 가져와 병합한다.

  4. 변경사항 업스트림 푸시:

    1
    
    git subtree push --prefix=<path> <repository-url> <branch>
    

    메인 저장소에서 서브트리 코드에 변경한 내용을 원본 저장소로 푸시할 수 있다.

구조적 차이점

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 서브모듈 구조
main-repo/
├── .git/
├── .gitmodules      # 서브모듈 참조 정보
├── project-files/
└── submodule-dir/   # 외부 저장소 참조
    └── .git         # 포인터 파일 (실제 .git은 부모의 .git/modules에 있음)

# 서브트리 구조
main-repo/
├── .git/
├── project-files/
└── subtree-dir/     # 일반 디렉토리로 취급
    └── files        # 모든 파일이 메인 저장소의 일부

장단점 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
장점• 코드 재사용성 높음
• 의존성 버전 고정으로 안정성 확보
• 저장소 크기가 작게 유지됨
• 접근 제어가 명확함
• 모듈별 독립적인 히스토리 유지
• 외부 저장소 변경에 영향 받지 않음
• 코드 공유 용이성
• 직접 수정 가능성
• 양방향 동기화 지원
• 별도 초기화 없이 즉시 사용 가능
• 기존 Git 명령어와 워크플로우 유지
• 단일 저장소로 CI/CD 파이프라인 간소화
단점• 복잡한 워크플로우
• 추가 학습 필요
• 초기화/업데이트 단계 필요
• 실수로 하위 모듈 변경 시 문제 발생 가능
• 원격 저장소 접근 불가 시 작업 제한
• 브랜치 전환 시 서브모듈 상태 관리 복잡
• 명령어가 복잡하고 기억하기 어려움
• 저장소 크기 증가
• 접근 제어 제한적
• 하위 저장소 위치를 기억해야 함
• 히스토리 오염으로 가독성 저하
• 원본 저장소 접근 권한 없을 때 업데이트 불가

3. 핵심 개념 및 비교 분석

주요 기능 및 역할

항목Git SubmoduleGit Subtree
버전 고정O (commit 단위)O (병합 시점 기준 snapshot)
코드 공유OO
업데이트 동기화수동수동 혹은 자동 스크립트화 가능
분리 배포 가능성높음 (완전 분리)낮음 (일체화됨)

목적 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
주요 목적외부 저장소에 대한 참조만 저장하여 프로젝트 관리외부 저장소의 코드를 메인 저장소에 통합
코드 분리코드 베이스를 분리된 상태로 유지코드 베이스를 하나로 통합
저장소 간 관계느슨한 결합 (Loose coupling) 유지더 강한 결합 (Tight coupling) 형성
버전 관리외부 저장소의 특정 버전 참조외부 저장소의 코드와 히스토리 병합

필요성 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
외부 라이브러리 관리외부 라이브러리의 업데이트 명시적 관리 가능외부 라이브러리 코드를 직접 통합하여 관리
여러 프로젝트에서 코드 공유코드를 여러 프로젝트에서 독립적으로 참조코드 복제본을 각 프로젝트에 포함
모듈화코드베이스 모듈화에 적합단일 코드베이스 관리에 적합
코드 기여외부 프로젝트에 직접 기여 용이변경사항을 다시 업스트림에 푸시하기 복잡할 수 있음

주요 기능 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
코드 포함 방식참조 링크만 포함코드 전체 복사 및 통합
히스토리 관리외부 저장소 히스토리와 분리외부 저장소 히스토리 통합
변경 추적메인 저장소와 서브모듈 변경 별도 추적모든 변경이 메인 저장소에서 추적
업데이트 방식명시적 업데이트 명령 필요일반적인 깃 명령으로 관리 가능
저장소 접근서브모듈에 접근하려면 별도 초기화 필요클론 후 즉시 모든 코드 접근 가능

역할 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
프로젝트 구조화독립적인 모듈로 구조화통합된 단일 구조로 관리
팀 협업팀 간 명확한 코드 소유권 분리단일 팀이 전체 코드베이스 관리
의존성 관리외부 의존성 명시적 관리의존성을 내부화하여 관리
코드 재사용여러 프로젝트 간 코드 재사용 촉진프로젝트별 코드 사본 관리

특징 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
학습 곡선비교적 가파른 학습 곡선더 직관적인 사용법
설정 복잡성초기 설정 복잡더 간단한 설정
독립성독립적인 저장소 유지메인 저장소와 통합
크기 영향메인 저장소 크기 작게 유지메인 저장소 크기 증가
팀 이해도팀원이 서브모듈 개념 이해 필요일반적인 깃 지식만으로 접근 가능
협업 관리어렵고 복잡단순함

분류에 따른 종류 및 유형

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
코드 통합 유형참조 기반 통합복사 기반 통합
사용 시나리오외부 의존성이 자주 변경되지 않는 경우외부 코드를 내부화하려는 경우
복잡성 수준복잡한 구성, 명시적 관리단순한 구성, 묵시적 관리
적합한 프로젝트 크기대규모 모듈화된 프로젝트중소규모 프로젝트

적합한 사용 사례

Git SubmoduleGit Subtree
컴포넌트나 서브프로젝트가 빠르게 변경되어 API 를 손상시킬 수 있을 때 안전을 위해 코드를 특정 커밋에 고정코드를 한 번만 외부 저장소에서 복사하거나 가끔씩만 가져오려는 경우
자주 업데이트되지 않는 의존성을 추적하고자 할 때팀이 명시적인 의존성 관리의 복잡성 없이 단순성을 선호할 때
외부 코드의 커밋 이력과 메인 프로젝트의 이력을 명확히 분리해야 할 때외부 코드를 프로젝트의 일부로 취급하고 관리하려 할 때
여러 프로젝트에서 공유하는 코드의 버전을 엄격하게 관리해야 할 때외부 코드를 수정하고 원본 저장소로 변경사항을 다시 쉽게 전달할 필요가 없을 때

실무 적용 예시

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
프레임워크 통합프레임워크를 서브모듈로 참조하여 업데이트 관리프레임워크 코드를 직접 통합하여 맞춤화
라이브러리 관리외부 라이브러리를 참조하여 버전 고정라이브러리 코드를 직접 통합하여 내부 수정 용이
마이크로서비스마이크로서비스 간 공통 코드 관리서비스별 독립적 코드베이스 유지
플러그인 시스템플러그인을 별도 저장소로 관리플러그인 코드를 메인 코드베이스에 통합
오픈소스 기여오픈소스 프로젝트를 서브모듈로 포함하여 기여오픈소스 코드 일부를 가져와 내부 사용
독립된 프로젝트 종속성Submodule서로 분리된 독립 유지가 필요함

실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
초기 설정git submodule add 명령으로 추가 필요git subtree add 명령으로 추가 필요
클론 후 작업git submodule initgit submodule update 필요별도 초기화 필요 없음
팀 교육팀원들에게 서브모듈 워크플로우 교육 필요일반적인 깃 워크플로우만 교육 필요
문서화서브모듈 사용에 대한 명확한 문서화 필요서브트리 명령어에 대한 문서화 필요
분리된 저장소 관리서브모듈 저장소 접근 권한 관리 필요중앙 저장소 접근 권한만 관리
외부 의존성 변경의존성 변경 시 명시적 업데이트 필요의존성 변경 시 병합 충돌 관리 필요

최적화하기 위한 고려사항 및 주의할 점

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
저장소 크기참조만 저장하여 메인 저장소 크기 작게 유지모든 코드를 복사하여 저장소 크기 증가
클론 시간서브모듈 초기화로 인해 클론 시간 증가초기 클론 시간 짧음
히스토리 검색분리된 히스토리로 검색 성능 향상통합된 히스토리로 검색 복잡성 증가
캐싱 효율성모듈별 캐싱으로 효율적인 작업 가능전체 저장소 변경으로 캐싱 효율 감소
병렬 작업모듈별 독립적 작업 가능단일 저장소 내 작업 의존성 증가
대규모 프로젝트대규모 프로젝트에서 모듈화로 성능 향상대규모 프로젝트에서 저장소 크기 문제 발생 가능
빌드 성능불필요한 모듈은 체크아웃 안 해도 됨전체 포함되므로 필요 시 디렉토리 분리 빌드
동기화 전략fetch + checkout 조합 최적화스크립트를 통한 업데이트 자동화
CI/CD서브모듈 경로 인식 문제 대응 필요일체형 관리로 구성 쉬움

개발자 경험 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
초기 설정 복잡성높음 - .gitmodules 파일 및 여러 단계 필요중간 - 단일 명령으로 시작 가능
일상적 사용복잡함 - 특별한 명령어 지식 필요단순함 - 일반 깃 워크플로우와 유사
학습 곡선가파름 - 특정 개념과 명령어 학습 필요완만함 - 기존 깃 지식으로 접근 가능
디버깅 용이성어려움 - 서브모듈 상태 추적이 복잡중간 - 일반 깃 디버깅 기술 적용 가능
팀 도입 난이도높음 - 모든 팀원이 서브모듈 개념 이해 필요낮음 - 일반 깃 지식으로 충분

성능 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
클론 속도느림 - 서브모듈 초기화/업데이트 필요빠름 - 모든 코드가 즉시 사용 가능
저장소 크기작음 - 참조만 저장큼 - 전체 코드 및 히스토리 저장
브랜치 전환 속도느림 - 서브모듈 업데이트 필요빠름 - 즉시 전환 가능
대규모 프로젝트 확장성좋음 - 모듈화로 성능 유지제한적 - 저장소 크기 증가로 성능 저하 가능
네트워크 요구사항높음 - 서브모듈 저장소 접근 필요낮음 - 클론 후 네트워크 접근 불필요

CI/CD 통합 비교

구분깃 서브모듈 (Git Submodule)깃 서브트리 (Git Subtree)
설정 복잡성높음 - 서브모듈 초기화 단계 필요낮음 - 추가 설정 불필요
빌드 안정성낮음 - 외부 저장소 의존으로 불안정 가능높음 - 모든 코드가 저장소 내에 있음
빌드 속도느림 - 서브모듈 초기화 시간 필요빠름 - 추가 초기화 불필요
의존성 추적명시적 - 특정 커밋 참조암시적 - 코드 통합으로 인한 추적 어려움
자동화 용이성어려움 - 복잡한 스크립트 필요쉬움 - 표준 깃 워크플로우 활용

최근근 동향

두 기술 모두 진화하고 있으며 다음과 같은 동향이 나타나고 있다:

  • 하이브리드 접근법: 모노레포와 결합하여 개발은 단일 저장소에서, 배포는 개별 저장소로 하는 방식이 인기를 얻고 있다.
  • 자동화 도구: CI/CD 파이프라인과의 통합과 자동화 도구를 통해 복잡한 워크플로우를 단순화하는 추세이다.
  • 대안 솔루션: git-subrepo 와 같이 두 기술의 장점을 결합한 새로운 대안 도구들이 등장하고 있다.
  • AI 기반 자동화: 병합 충돌 해결 및 의존성 관리를 위한 AI 기반 도구가 발전하고 있다.
  • 보안 강화: 의존성 취약점 관리와 접근 제어를 강화하는 추가 기능들이 도입되고 있다.

결론적으로, Git Submodule 은 외부 의존성을 특정 버전으로 고정하고 독립적으로 관리하는 데 적합하며, Git Subtree 는 외부 코드를 프로젝트에 통합하고 단일 저장소처럼 관리하는 데 더 적합하다. 프로젝트의 특성과 팀의 워크플로우에 따라 적절한 도구를 선택하거나 하이브리드 접근법을 고려하는 것이 중요하다.

추가 학습 내용

학습 주제서브모듈 관련 내용서브트리 관련 내용
고급 깃 명령어git submodule foreach, git submodule sync, git submodule deinitgit subtree split, git subtree merge, git subtree push
충돌 해결서브모듈 참조 충돌 해결 방법서브트리 병합 충돌 해결 전략
CI/CD 통합서브모듈이 있는 프로젝트의 CI/CD 파이프라인 구성서브트리 프로젝트의 CI/CD 자동화
모노레포 전략서브모듈을 활용한 모노레포 관리서브트리 기반 모노레포 아키텍처
마이그레이션일반 저장소에서 서브모듈로 전환 방법서브모듈에서 서브트리로 마이그레이션
보안 고려사항서브모듈의 보안 취약점 관리서브트리 코드의 보안 감사
분산 팀 협업분산된 팀에서 서브모듈 관리 전략서브트리 기반 협업 워크플로우
히스토리 관리서브모듈 히스토리와 메인 저장소 히스토리 통합서브트리 커밋 히스토리 관리 및 정리
성능 최적화대규모 서브모듈 프로젝트 성능 향상 기법서브트리 저장소 크기 및 성능 최적화
자동화 도구서브모듈 관리 자동화 스크립트 개발서브트리 워크플로우 자동화 도구

용어 정리

용어설명
CBD컴포넌트 단위 개발 (예: 라이브러리 분리)
SBD시스템 단위 개발 (예: 통합 코드베이스)
깃 서브모듈 (Git Submodule)메인 저장소 내에 다른 저장소에 대한 참조를 포함하는 기능으로, 외부 저장소의 특정 커밋을 참조
깃 서브트리 (Git Subtree)외부 저장소의 코드를 메인 저장소에 복사하여 통합하는 기능으로, 외부 코드의 전체 히스토리를 메인 저장소에 병합
.gitmodules서브모듈 URL, 로컬 경로, 브랜치 정보 등 서브모듈 구성 정보를 저장하는 설정 파일
서브모듈 초기화 (Submodule Init)서브모듈의 로컬 구성을 초기화하는 과정 (git submodule init)
서브모듈 업데이트 (Submodule Update)서브모듈 코드를 지정된 커밋으로 업데이트하는 과정 (git submodule update)
서브트리 추가 (Subtree Add)외부 저장소를 서브트리로 추가하는 과정 (git subtree add)
서브트리 병합 (Subtree Merge)서브트리 변경사항을 메인 저장소에 병합하는 과정 (git subtree merge)
스쿼시 (Squash)여러 커밋을 하나의 커밋으로 압축하는 것으로, 서브트리에서 히스토리 관리를 위해 자주 사용 (--squash)
업스트림 (Upstream)원본 코드가 있는 저장소로, 서브모듈이나 서브트리에서 변경사항을 푸시하는 대상
모노레포 (Monorepo)여러 프로젝트나 모듈을 단일 저장소에서 관리하는 방식

참고 및 출처