여러 프로젝트를 동시에 개발하다 보면 공통 유틸리티 함수나 디자인 시스템, 공용 라이브러리를 여러 리포지토리에서 함께 써야 하는 순간이 꼭 찾아오죠. 예전의 저는 별다른 고민 없이 코드를 이리저리 ‘복사-붙여넣기’ 하곤 했답니다. 그런데 라이브러리 코드에 치명적인 버그가 하나 발견되자마자 서너 군데 저장소를 일일이 찾아다니며 수정하느라 밤샘 작업을 피하지 못했던 뼈아픈 기억이 있어요… 😭 솔직히 말해서 코드 동기화 지옥이 따로 없더라고요.
이런 번거로움과 유지보수의 악몽을 한 번에 해결해 주는 도구가 바로 Git의 서브모듈(Submodule) 기능이에요. 하나의 Git 저장소 안에서 또 다른 독립된 Git 저장소를 마치 일반 하위 폴더처럼 품어 관리할 수 있도록 도와준답니다. 오늘 포스팅에서는 서브모듈의 핵심 개념부터 프로젝트에 추가하고 클론하는 실전 명령어, 그리고 실무에서 자주 겪는 주의사항까지 친절하게 알려드릴게요! 😊
서브모듈(Submodule)이란? 왜 필요할까요? 🤔
서브모듈은 쉽게 말해 ‘저장소 안의 또 다른 저장소’라고 이해하시면 돼요. 메인 프로젝트(상위 저장소)의 형상 관리를 유지하면서도, 특정 디렉터리를 외부의 독립된 깃 저장소의 특정 커밋과 다이렉트로 연결해 주는 깃의 내장 기능입니다.
단순히 외부 코드를 다운로드해서 파일째로 커밋하는 것과 서브모듈은 무엇이 다를까요? 코드를 직접 복사해 두면 원본 저장소에서 새로운 기능이 추가되거나 버그가 고쳐졌을 때 내 프로젝트로 가져오기가 매우 번거롭고 충돌 위험도 큽니다. 반면 서브모듈을 사용하면 외부 프로젝트의 고유한 커밋 이력(Git History)을 온전히 보존하면서, 언제든지 원하는 최신 커밋이나 안정적인 특정 버전으로 자유롭게 동기화할 수 있어요.
상위 저장소는 서브모듈 디렉터리의 실제 파일 내용 전체를 추적하지 않습니다. 대신 서브모듈의 원격 주소 정보가 담긴
.gitmodules 파일과 서브모듈 저장소의 ‘특정 커밋 해시(Commit Hash)’만을 포인터 형태로 기록합니다. 따라서 외부 저장소에 새 커밋이 올라와도 상위 프로젝트가 명시적으로 갱신하지 않는 한 기존 상태가 안정적으로 유지됩니다.
다른 프로젝트를 쏙! 서브모듈 추가 및 클론 명령어 📊
서브모듈을 사용할 때 실무에서 마주치는 작업은 크게 두 가지로 나뉩니다. 첫째는 기존 프로젝트에 외부 저장소를 새롭게 서브모듈로 추가하는 것이고, 둘째는 이미 서브모듈이 구성된 프로젝트를 새 환경이나 다른 팀원이 내려받는(Clone) 것이에요.
명령어 구조 자체는 간단하지만 옵션을 빠뜨리면 빈 폴더만 생성되어 당황하기 쉬워요. 상황별 필수 명령어와 핵심 역할을 표로 정리해 드릴게요.
상황별 서브모듈 필수 명령어 비교
| 구분 | 핵심 명령어 | 작업 설명 | 핵심 주의점 |
|---|---|---|---|
| 서브모듈 신규 추가 | git submodule add <URL> [경로] |
외부 저장소를 지정 경로에 하위 모듈로 등록 | .gitmodules 파일 생성 확인 필수 |
| 저장소 전체 클론 | git clone --recurse-submodules <URL> |
메인 저장소와 하위 서브모듈을 한 번에 일괄 다운로드 | 옵션 누락 시 서브모듈 폴더가 비어있음 |
| 사후 서브모듈 다운 | git submodule update --init --recursive |
일반 클론 후 비어있는 서브모듈 초기화 및 동기화 | 중첩 서브모듈까지 받으려면 –recursive 필요 |
| 최신 원격 커밋 반영 | git submodule update --remote |
서브모듈 원격 저장소의 최신 변경 내용을 가져옴 | 갱신 후 메인 저장소에서도 커밋 필요 |
서브모듈이 포함된 프로젝트를 평소처럼 일반적인
git clone <URL>로만 내려받으면 서브모듈 디렉터리는 내용이 전혀 없는 텅 빈 껍데기 폴더로 생성됩니다. 프로젝트를 한 번에 온전히 빌드하려면 반드시 --recurse-submodules 플래그를 붙여 클론해 주셔야 합니다!
실전 적용을 돕는 서브모듈 명령어 가이드 및 도구 🧮
서브모듈을 추가할 때 실행되는 기본 공식과 내부 원리를 명확히 짚고 넘어가면 터미널 작업이 훨씬 쉬워집니다.
📝 서브모듈 신규 추가 기본 공식
기본 명령어 구조 = git submodule add [원격 저장소 URL] [설치할 로컬 폴더 경로]
실제 터미널에서 작업이 진행되는 흐름은 다음과 같습니다:
1) 첫 번째 단계: 메인 프로젝트 루트 디렉터리로 터미널 위치를 이동합니다.
2) 두 번째 단계: 외부 라이브러리 저장소 URL과 배치할 폴더명을 지정하여 add 명령어를 실행합니다.
→ 실행 결과: 로컬에 폴더가 복제되고, URL과 경로를 매핑한 .gitmodules 파일이 자동 생성됩니다.
🔢 Git 서브모듈 명령어 자동 생성기
실무 작업 시 필수 꿀팁과 Detached HEAD 주의점 👩💼👨💻
서브모듈을 처음 쓰시는 분들이 가장 흔하게 겪는 패닉 상황이 있어요. 바로 서브모듈 폴더 안으로 이동해 터미널에 git status를 입력했을 때 나타나는 ‘HEAD detached at…’이라는 경고 메시지예요! 😱
이것은 오류가 아니라 서브모듈의 지극히 정상적인 작동 원리입니다. 서브모듈은 특정 브랜치의 최신 상태를 따라가는 것이 아니라, 상위 저장소가 기록해 둔 ‘특정 커밋 해시’에 고정되어 체크아웃되기 때문이에요. 하지만 브랜치가 없는 상태(Detached HEAD)에서 코드를 수정하고 커밋해 버리면, 나중에 다른 브랜치로 이동했을 때 해당 커밋이 공중에 붕 떠버려서 소중한 작업물이 유실될 수 있습니다.
따라서 서브모듈 디렉터리 내부에서 코드를 수정해야 할 때는 반드시 해당 폴더로 진입한 뒤 git checkout main(또는 작업용 브랜치)을 실행하여 정상 브랜치 상태로 만든 다음 작업을 진행해야 합니다.
수정 사항을 원격 저장소에 푸시(Push)할 때는 반드시 ‘서브모듈 커밋/푸시 → 메인 프로젝트 커밋/푸시’의 황금 순서를 지켜야 합니다! 만약 메인 프로젝트를 먼저 푸시해 버리면, 팀원들은 원격 저장소에 아직 존재하지 않는 서브모듈 커밋 포인터를 바라보게 되어 빌드 실패 오류를 겪게 됩니다.
실전 예시: 공통 UI 라이브러리(Common-UI) 분리 적용기 📚
실제 서비스 환경에서 서브모듈이 어떻게 프로젝트 구조를 깔끔하게 만들어주는지 구체적인 실무 시나리오로 살펴볼게요.
개발팀의 실제 고민 상황
- 상황: 사용자 웹 서비스(Web-Client)와 관리자 백오피스(Admin-Web)에서 버튼, 모달 등 동일한 UI 컴포넌트를 사용 중
- 문제점: 두 프로젝트에 UI 코드를 복붙해 두었더니, 버튼 색상 하나를 변경할 때마다 양쪽 프로젝트에서 중복 수정 발생
서브모듈 적용 과정
1) 공통 컴포넌트만 모아 common-ui.git이라는 별도의 독립 저장소를 새롭게 생성
2) 웹 서비스 프로젝트 루트에서 git submodule add https://github.com/myteam/common-ui.git src/shared/ui 실행
3) 관리자 프로젝트 루트에서도 동일하게 서브모듈 추가 명령어 실행
도입 후 최종 결과
– 코드 중복 제거: 공통 UI 코드베이스가 단 한 곳으로 통합되어 관리 포인트 일원화
– 동기화 효율 극대화: 공통 UI 저장소에서 기능 수정 후, 각 프로젝트는 git submodule update --remote 한 줄로 즉시 반영 완료
이처럼 서브모듈을 활용하면 사내 패키지 매니저(NPM Private Registry 등)를 거창하게 구축하지 않고도 손쉽게 여러 프로젝트 간의 공통 코드를 모듈화하여 관리할 수 있습니다.
마무리: 핵심 내용 요약 📝
오늘은 Git 서브모듈의 탄생 배경부터 프로젝트에 외부 저장소를 포함시키는 구체적인 방법, 그리고 실무자가 꼭 기억해야 할 협업 노하우까지 차근차근 살펴보았습니다.
처음에는 커밋 포인터라는 개념이나 Detached HEAD 상태가 다소 낯설고 까다롭게 느껴질 수 있지만, 저장소를 깔끔하게 분리하고 공통 코드를 체계적으로 다루는 데 있어 서브모듈만큼 강력한 기본기도 드뭅니다. 작은 프로젝트의 공통 유틸리티부터 직접 서브모듈로 분리해 보며 개발 생산성을 한 단계 높여보시는 것은 어떨까요? 실습해 보시다가 잘 안 풀리는 점이나 궁금한 내용이 있다면 언제든 편하게 댓글로 물어봐주세요~ 😊
서브모듈 핵심 정리 카드
자주 묻는 질문 ❓
git submodule update --init --recursive 명령어를 실행하시면 비어있던 서브모듈 파일들을 즉시 정상 다운로드할 수 있습니다.git submodule update --remote 명령어를 실행한 후 메인 프로젝트에서 변경된 커밋 포인터를 새롭게 커밋 및 푸시해 주어야 합니다.git checkout main 등으로 작업 브랜치로 전환한 후 커밋과 푸시를 진행해야 작업 내용 유실을 막을 수 있습니다.git submodule deinit -f [경로]를 실행해 서브모듈 등록을 해제하고, git rm -f [경로]를 통해 작업 트리 및 .gitmodules에서 제거한 뒤 .git/modules/[경로] 캐시를 삭제해 주시면 완전히 제거됩니다.