💡 이 글은 Submodule과 Subtree를 활용하여, Firestore와 같은 외부 종속성의 코드를 안정적으로 관리하면서 프로젝트를 원활히 통합하는 방법에 대해서 소개합니다.
Firestore와 같은 외부 종속성의 코드를 클라이언트 내부 프로젝트에서 직접 수정하고 병합하는 것은 프로젝트의 안정성을 위협할 수 있습니다.
지난 2개월 간, Firestore로 API를 개발하여 2개의 클라이언트 레포지토리에 동시 사용할 수 있도록 Submodule과 Subtree를 관리해왔습니다.
이 경험을 토대로, 코드를 완전히 분리하여 외부 종속성의 코드를 안정적으로 유지하면서도 서로 다른 프로젝트들의 원활한 통합을 관리할 수 있는 Submodule과 Subtree에 대해 배우고, 상황에 따라 어떤 방법을 선택해야 하는지에 대해 이해를 돕고자 작성하게 되었습니다.
Submodule과 Subtree는 외부 레포지토리를 현재 레포지토리에 포함시키는 방법입니다.
Submodule은 외부 레포지토리를 다른 디렉토리에 포함시키고, 외부 레포지토리의 변경 사항을 추적할 수 있습니다.
반면 Subtree는 외부 레포지토리를 현재 레포지토리의 하위 디렉토리로 포함시키는 구조입니다.
사실 이렇게만 읽었을 때는 이해가 어려울 수 있습니다.
저의 경험을 바탕으로 예시를 들겠습니다.
저는 Firebase의 Firestore를 사용하여 데이터베이스에 직접 접근하여 CRUD 작업을 수행하는 API를 A 레포지토리에서 개발하고 있습니다.
이 API는 아래의 구조처럼 앱을 개발하고 있는 B와 웹을 개발하고 있는 C 레포지토리에서 사용해야 합니다.
이때, B와 C 레포지토리가 A 코드로 개발한 API 기능을 가져오는 방법을 고려해야 했습니다.
외부 코드를 통합하는 방법은 다음의 경우들이 있을 것입니다.
1번은 A(외부 코드)의 업스트림 변경 사항이 손실된다는 단점이 있습니다.
2번은 origin 코드가 배포되는 모든 곳에서 설치 및 버전 관리가 필요하다는 단점이 있습니다.
두 가지 통합 방법 모두 외부 레포지토리에 대한 편집 및 변경 사항을 추적할 수 없습니다.
그래서 외부 레포지토리의 변경 사항을 추적 및 업데이트할 수 있는 Git Submodule 방식을 사용하게 되었습니다.
그런데, 왜 Subtree가 아닌 Submodule을 선택했을까요?
차이점은 B와 C가 외부 코드(A)를 직접 수정하여 배포할 수 있는가 없는가에 있습니다.
Git Submodule은 다른 외부 레포지토리의 특정 커밋을 추적합니다.
또한, 자동으로 업데이트 되지 않습니다.
이는 서버 쪽에서도 안정화된 코드를 기반으로 클라이언트가 개발할 수 있도록 보장함과 동시에 서버쪽에서 자체 추가적인 개발을 할 수 있도록 해줍니다.
또한, Submodule을 사용하면 외부 코드를 가져와서 사용할 수 있지만, 내부적으로 외부 코드를 수정했을 때, 이 수정 내역이 외부 코드의 origin에 반영되지 않습니다.
즉, B와 C는 로컬에서는 수정할 수 있지만, 이 수정 사항이 A 레포지토리에 반영되지 않습니다.
반면에 Subtree를 사용한다면, B와 C는 A의 코드를 가져와서 수정한 사항을 A의 origin에 반영할 수 있습니다. 이는 B와 C가 A의 코드를 직접 수정하여 개선할 수 있는 유연성을 제공합니다.
더 자세한 이해를 돕기 위해 다음 실습을 준비해보았습니다.
실제 프로젝트에서 Submodule과 Subtree를 사용하여 Firestore와 같은 외부 종속성을 관리하는 방법에 대해 실습하고, 결과물을 확인해보겠습니다.
이를 통해 어떻게 외부 코드를 프로젝트에 통합하고 관리할 수 있는지에 대한 이해를 높일 수 있을 것입니다.
git init
명령어를 실행하여 Git 레포지토리를 초기화합니다.클라이언트 프로젝트에 서버 프로젝트(lib_repository_url
) 를 submodule로 추가합니다.
git submodule add <lib_repository_url> <path>
path: 클라이언트 프로젝트(내부 프로젝트) 내의 폴더 경로
이 명령은 클라이언트 프로젝트에 서버 프로젝트를 path
경로에 서브모듈로 추가합니다.
아래 명령어로 submodule을 초기화 합니다.
git submodule update --init --recursive
클라이언트 프로젝트에서 서버 프로젝트 디렉토리를 확인합니다.
서버 프로젝트의 변경 사항을 추적하고 동기화하기 위해 서버 프로젝트 디렉토리로 이동하여 변경 사항을 커밋하고 푸시합니다.
submodule의 변경 사항을 가져옵니다.
git submodule update --remote <path>
git submodule update --remote
이 명령들은 submodule의 최신 변경 사항을 가져옵니다.
클라이언트 프로젝트에서 다음명령어를 사용하여 서버 프로젝트를 Subtree로 추가합니다.
git subtree add --prefix=<path> <클라이언트 프로젝트 레포지토리 URL> master
이 명령은 서버 레포지토리의 최신 변경 사항을 서버 프로젝트의 path
subtree에 통합합니다.
클라이언트 프로젝트에서 서버 프로젝트 디렉토리를 확인합니다.
서브트리에 대한 변경 사항을 반영합니다.
git subtree push --prefix=<path> <lib_repository_url> master
저는 Node.js 환경에서 Firestore를 활용한 CRUD 메서드를 클래스 형태로 개발하고 있습니다. 이 메서드들은 클라이언트에서 사용하기 위해 tanstack-query(react-query) hook으로도 개발되었습니다. 이렇게 구현한 API를 클라이언트에 제공하기 위해 Submodule 형태로 관리하고 있습니다.
그러나 API의 기능을 테스트할 때, CRUD 메서드의 단위 테스트 뿐만 아니라 클라이언트 환경에서 query가 올바르게 동작하는지에 대한 테스트도 필요했습니다.
Subtree가 이 필요성을 해결해주었습니다. 서버 코드를 클라이언트 프로젝트에 통합하고 동시에 클라이언트 환경에서 서버 API를 개발하고 query를 테스트할 수 있었습니다.
이러한 경험을 통해, 프로젝트의 요구 사항에 따라 Submodule과 Subtree를 유연하게 활용하여 코드를 관리하고 협업하는 방법에 대해 배우게 되었습니다.
저와 비슷한 상황이신 분들도 제 글을 통해서 도움이 되셨으면 좋겠습니다!
참고 자료
https://www.atlassian.com/ko/git/tutorials/git-submodule
https://www.atlassian.com/ko/git/tutorials/git-subtree
https://git-scm.com/book/en/v2/Git-Tools-Submodules