안녕하세요? 이번 시간에는 서브모듈을 활용해 민감한 정보가 있는 환경 변수를 관리하는 방법에 대해 알아보겠습니다.

image

프로젝트를 개발하다보면 dev 환경에서 사용하는 변수들과 prod 환경에서 사용하는 변수들이 다릅니다. 예를 들어 dev 환경에서 데이터베이스 urljdbc:mysql://localhost:3306/test?serverTimezone=UTC 이지만 prod 환경에서는 rds로 데이터베이스를 생성했다면 jdbc:mysql://test.abcd.rds.amazonaws.com:3306/test 와 같은 형식일 것입니다. 따라서 환경 변수를 분리할 뿐만 아니라 프로덕션에 적용되는 변수들은 노출되지 않도록 주의해야합니다.


submodule 추가

Git에서 제공하는 기능 중 하나인 서브모듈을 통해 변수를 관리해보겠습니다. 서브모듈은 하나의 Git 저장소 안에 다른 Git 저장소를 포함시키는 방법을 말합니다. 상위 Repository인 backend-submodule-test에서 하위 Repository인 submodule-test을 포함하도록 하겠습니다.

우선 상위 레포지토리에서 아래 명령어를 입력하면 .gitmodules 파일이 생성됩니다.

git submodule add https://github.com/devbelly/submodule-test.git
image

기본적으로 서브모듈은 프로젝트 저장소의 이름으로 디렉토리를 만듭니다. 다만.gitmodules 파일에 있는 URL은 조건에 맞는 사람이라면 누구든지 clone하고 fetch할 수 있어야합니다. 실제 프로젝트에서 submodule-test를 private으로 설정했다면 Collaborators에 같이 개발하는 동료를 추가해야 합니다.


image

이후 푸시를 하면 서브모듈의 특정 커밋을 가리키는 것을 확인할 수 있습니다.


submodule을 포함하는 프로젝트 Clone하기

만약 서브모듈을 위와 같이 추가한 상태에서 처음 동료가 서브모듈을 포함한 프로젝트를 클론 받는 상황을 가정해보겠습니다.

image

git clone을 하더라도 서브모듈 디렉토리는 비어있습니다. 서브모듈과 관련된 두 명령을 실행해야 완전히 클론과정이 끝나게 됩니다. 처음에는 git submodule init으로 .gitmodules 파일에 정의된 서브모듈 설정을 현재 Git 프로젝트에 추가해야합니다.


image

.git/config에 서브모듈 관련 설정이 추가된 것을 확인할 수 있습니다.


image

이후 git submodule update 명령으로 서브모듈의 리모트 저장소에서 데이터를 가져오고 서브모듈을 포함한 프로젝트의 현재 스냅샷에서 Checkout 해야 할 커밋 정보를 가져와서 서브모듈 프로젝트에 대한 Checkout을 합니다.


주의할 점

git submodule update는 기본적으로 특정 커밋으로 Checkout을 하므로 서브모듈 로컬 저장소는 Detached HEAD 상태가 됩니다. 이 상태에서는 수정한 내용에 대해 추적이 제대로 이뤄지지 않으므로 작업 브랜치로 Checkout을 해줘야합니다.

따라서 git submodule update --remote 명령어를 수행했다면 서브모듈에서 git checkout main을 해야합니다.

이 과정이 번거롭다면 git submodule update --remote --merge 옵션을 사용하면 됩니다.


설정 파일 옮기기

서브모듈을 업데이트 했다면 가져온 application-test.yaml 파일을 적절한 위치로 옮겨줘야합니다. build.gradle.kts에 아래 내용을 추가하면 파일이 옮겨집니다. 파일을 옮긴 뒤에 적절하게 gitignore 설정으로 프로덕션과 관련된 파일들을 제외하면 안전하게 환경 변수를 사용할 수 있습니다.

// build.gradle.tks
tasks {  
    val copySecret by register<Copy>("copySecret") {  
       from("./submodule-test") // 서브모듈 디렉토리 경로  
       include("application*.yaml") // 복사할 파일들  
       into("./src/main/resources") // 복사 위치  
    }  
  
    named("processResources") {  
       dependsOn(copySecret)  
    }  
}

참고