프로젝트에는 데이터베이스 관련 설정, AWS 관련 설정, API Key와 같은 민감 데이터가 포함될 수 있다. 당연히 이와 같은 정보는 하드 코딩해선 안 되고, 별도의 설정 파일로 관리해야 한다. 그래서 SpringBoot에서는 application.properties 파일이나, application.yml 파일을 이용해 설정을 관리하는 것이 일반적이다.
설정 파일을 별도로 관리함으로써 얻을 수 있는 또 하나의 이점은, 각 환경에 따라 다른 설정을 적용할 수 있다는 것이다. 즉, 로컬 환경(local-env), 개발 환경(dev-env), 운영 환경(prod-env), 테스트 환경(test-env)별로 다른 설정을 사용할 수 있게 된다.
문제는 설정 파일을 팀원들과 어떻게 공유할 것인가이다. 설정 파일은 다른 사람에겐 공개되지 않으면서, 팀원들 간에는 원활히 공유되어야 한다. 지금까지는 설정 파일을 단순히 gitignore 하는 방법에 대해서만 소개했었는데, 이러한 방식은 팀 프로젝트에서 적합하지 않을 수 있다. 그래서 이번 포스팅에서는 설정 파일을 Github Organization에 업로드하면서, 다른 사람에게는 공개되지 않도록(private) 설정하는 방법에 대해 알아보도록 하겠다.
설정 파일을 비공개로 설정하는 방법은 아래와 같다.
즉, 설정 파일을 별도의 Private Repository에 업로드하되, 프로젝트의 메인 레포지토리(Public Repository)와 연동하기 위해 Submodule을 이용한다는 것이다. 자세한 방법은 직접 실습해보면서 알아보기로 하자.
※ Submodule
Git 저장소 안에 다른 Git 저장소를 디렉토리로 분리해 넣는 것을 서브 모듈이라 한다. 서브 모듈을 이용하면 독립된 Git 저장소를 Clone 하여 다른 Git 저장소 안에 포함할 수 있으며, 각 저장소의 커밋은 독립적으로 관리된다.
깃허브와 연동된 스프링부트 프로젝트 및 깃허브 Organization이 이미 존재한다면, 이 단계는 생략해도 된다.
① 아래의 포스팅을 참조하여 실습에 사용할스프링부트 프로젝트를 생성 및 실행한다.
>> 스프링 개발환경 구축하기
② 아래의 포스팅을 참조하여 스프링부트 프로젝트를 깃허브와 연동한다.
>> IntelliJ와 Github 연동하기
③ 아래의 포스팅을 참조하여 Github Organization을 생성한다.
>> Github Organization 생성하기
④ application.properties 파일을 삭제한다.
⑤ Github Organization에 프로젝트를 업로드한다.
① 방금 생성한 Main Repository를 Fork한다.
② 아래의 포스팅을 참조하여 Fork 된 개인 레포지토리를 Clone 하여 IntelliJ로 불러온다.
>> Git Clone 하여 IntelliJ로 불러오기
③ 설정 파일을 관리할 Private Repository를 생성한다.
④ Private Repository의 HTTPS 주소를 복사한다.
⑤ IntelliJ 터미널에 아래의 명령을 입력하여 Submodule을 추가한다.
git submodule add {방금 복사한 repository의 주소}
⑥ 추가된 Submodule을 확인한다.
⑦ gitmodules 파일도 생성되는 것을 확인할 수 있다.
⑧ Fork된 개인 레포지토리로 Commit 및 Push 한다.
① 아래의 포스팅을 참조하여 Organization Repository로 PR을 보낸다.
>> Organization Repository에 Pull Request 보내기
② Merge가 완료되면, Organization의 Main Repository에서 Submodule로 연결된 Private Repository를 확인할 수 있게 된다.
③ Microsoft Edge를 이용해 Organization Repository의 HTTPS 주소로 접근해보자.
④ 공개되어 있는 일반 디렉토리에는 접근할 수 있지만, config 디렉토리에는 접근하지 못하는 것을 확인할 수 있다.
메인 레포지토리에서 서브 모듈을 확인할 수는 있지만, 서브 모듈 내의 파일을 변경하는 것은 불가능하다. 이는 서브 모듈의 디렉토리가 메인 레포지토리의 관리 대상이 아니기 때문이다. 즉, 민감 데이터를 포함하는 파일은 Private Repository에 의해서만 관리될 수 있다.
이 때, Main Repository와 Private Repository가 서로 동기화되지 않는 문제가 발생한다. 그 이유는 Main Repository에서 Private Repository의 Commit Hash 값을 참조하는 구조이기 때문이다. 즉, Private Repository가 업데이트 되어 새로운 Commit Hash 값을 갖게 되더라도, Main Repository에서는 여전히 예전 Commit Hash 값을 참조하게 된다는 것이다. 따라서, Main Repository가 참조하는 Commit Hash 값을 최신화하는 동기화 작업이 필요하다. 동기화는 아래와 같은 방법으로 진행된다.
① Submodule 디렉토리 하위로, yml 파일을 추가한다.
② Commit 후 본인의 Private Repository로 Push 한다.
cd config
git status
git add .
git commit -m 'add config file'
git push
③ config 레포지토리(Private Repository)에 설정 파일이 잘 업로드 되었는지 확인한다.
④ 이제 메인 레포지토리에서 참조하는 서브 모듈 디렉토리를 클릭해보자.
⑤ 서브 모듈이 최신화 되어 있는지 확인하는 방법은 Commit Hash 값이 동일한지 확인하는 것이다.
⑥ 서브 모듈을 최신화하기 위해 IntelliJ 터미널에 아래의 명령을 입력한다.
cd .. // 서브 모듈에서 빠져나온다.
git submodule update --remote
git status
git add .
git commit -m 'add config file'
git push
⑦ 개인 레포지토리에서 PR을 생성한 후 Organization에서 Merge 한다.
⑧ Main Repository에 적용된 Commit Hash 값이, Private Repository에 적용된 Commit Hash 값(7239a96)과 동일한지 확인한다.
⑨ 설정 파일을 수정할 때마다 반드시, Private Repository에 먼저 Push 한 후 Main Repository에서 최신화해야 한다.
⑩ 팀원이 업데이트 한 설정 파일을 로컬로 받아오고 싶다면, 아래의 명령을 입력하면 된다.
cd config
git pull --recurse-submodules {private repository의 HTTPS 주소}
설정 파일은 원칙적으로 src/main/resources에 위치해야 하지만, 현재는 임의로 생성한 Submodule 디렉토리 내부에 위치하고 있다. 따라서, Submodule 디렉토리에 있는 설정 파일을 src/main/resources로 가져오기 위한 설정을 진행해야 한다. 그 방법은 아래와 같다.
① Main Repository에 있는 설정 파일은 모두 삭제한다.
② build.gradle 파일에 아래의 코드를 추가한다.
processResources.dependsOn('copySecret')
tasks.register('copySecret', Copy) {
from './config' // 서브 모듈 디렉토리 경로
include "*.yml" // 설정 파일 복사
into 'src/main/resources' // 붙여넣을 위치
}
③ Company 테이블을 생성하는 코드가 잘 실행되는지 확인해보자.