사고가 발생했다.

어제 배포한 앱에서 발생한 사고였다. 구글 로그인 기능이 동작하지 않았다. 확인해보니 구글 로그인 라이브러리 초기화 시 사용하는 토큰 값이 잘못되어 있었다. 해당 값은 환경 변수에 넣어서 관리하고 있었는데, 빌드한 사람(본인)의 환경 변수 최신화가 제대로 이뤄지지 않았던 것이다. 개발/테스트 환경에서는 해당 값이 문제가 없었기 때문에, 프로덕션 레벨까지 배포되고 나서야 문제를 인지할 수 있었다.

부랴부랴 환경 변수를 최신화해서 앱을 재배포했다. 불행 중 다행으로 아직 사용자가 많지 않은 제품이었기에, 큰 사고로 이어지지는 않았다. 하지만 사고는 사고다. 5인 미만의 작은 팀이라 별다른 도구 없이도 잘 관리할 거라 생각했던 안일함이 사고를 불러왔다. 그리고 이대로라면 앞으로 같은 일이 반복되지 말란 법도 없었다.

그래서 우리는 이 사고의 원인을 근본적으로 해결할 방법을 찾기로 했다.

현황

우리 앱은 React Native로 구현되었다. 환경변수 사용에는 react-native-config 라이브러리를 사용한다.

앱은 안드로이드로만 배포된다. 추후에 iOS에도 앱을 출시할 생각이지만, 당장은 몇몇 사정 때문에 출시하지 않고 있다. (따라서 해결책을 찾을 때는 안드로이드 빌드만 고려할 생각이다. iOS에 대해서는 미래의 자신에게 맡기기로 했다.)

환경 변수(Environment Variables)에는 보통 각 환경마다 다르게 적용되는 상수 값들이 들어 있다. 프로젝트의 성격에 따라 다르겠지만 그 값들에는 DB 접속 정보, 서드 파티 접속 정보 같이 보안상 민감한 정보들도 포함하고 있다. 따라서 환경 변수를 관리하는 파일을 코드 저장소에서 제외하도록 권장되고 있다. (보다 자세한 정보는 The Twelve-Factor App3번 항목을 참고)

권장된 내용처럼 우리도 환경변수 파일들을 모두 코드 저장소에서 제외했다. 그리고 값이 변경될 경우 (인원이 많지 않다 보니) slack 등의 메신저를 통해 값을 주고 받아 각자 최신화를 해왔다.

팀원들 모두 이것이 문제라고 인지하고는 있었지만, 당장은 고치지 않아도 된다고 생각하고 있었다.

어떻게 해결해야 할까?

첫번째 방법으로는 툴을 쓰는 것이다. Vault가 가장 널리 쓰이는 툴인 듯 하다. EnvKey, Envault 등의 대체제들도 눈에 띈다.

하지만 일단 툴은 사용하지 않는다. 급박하게 돌아가고 있는 현재 스프린트에 최대한 지장을 주지 않는 선에서 빠르게 적용해야 하기 때문이다. 더군다나 보안과 관련된 툴을 급하게 적용하고 싶지는 않다.

두번째 방법은 비공개 코드 저장소를 활용하는 것이다. 이미 익숙한 툴이고, 쉘스크립트를 통해 자동화하기도 쉽다.

비공개 코드 저장소

github을 사용했다. 프로젝트의 루트 디렉토리에 환경변수 파일을 모두 저장했다.

.
+-- .env
+-- .env.dev
+-- .env.staging
+-- .env.production

쉘스크립트

빌드 전에 환경 변수 파일들을 다운로드 받는다. 정확히는 위에서 만든 코드 저장소에서 clone 혹은 pull 로 코드를 받아온다.

# $1에는 dev/staging/production 등 환경 이름이 들어간다.
ENVFILE_PATH="./env/.env.$1"
if [ -f "$ENVFILE_PATH" ]; then
  # 파일이 존재하면 코드 저장소로부터 파일을 업데이트한다.
  cd env && git checkout master && git pull origin master && cd ../
else
  # 파일이 존재하지 않으면 코드 저장소로부터 파일을 다운로드 받는다.
  git clone https://github.com/OWNER/PROJECT.git ./env
fi

(당연하지만 ENVFILE_PATH로 사용된 경로 및 파일은 .gitignore에 포함되어 있어야 한다.)

그다음에는 안드로이드 빌드 명령어를 실행한다.

# $2에는 assembleReleasedev 등 안드로이드 빌드 시 쓰이는 빌드 타입이 들어간다.
cd ./android && ENVFILE=env/.env.$1 ./gradlew $2 && cd ../

보너스로 빌드가 완료되면 자동으로 폴더를 열어주자.

BUILD_DIR="$(echo ${2:8} | tr '[A-Z]' '[a-z]')"
open ./android/app/build/outputs/apk/$BUILD_DIR

마지막으로 작성이 완료된 쉘스크립트를 package.json 에 등록하면 된다.

// package.json
{
  // ...
  "scripts": {
    // ...
    "build-android-dev": "bash scripts/build-android.sh dev assembleReleasedev",
    "build-android-staging": "bash scripts/build-android.sh staging assembleReleasedev",
    // ...
  }
}

이제 빌드 시 위에 등록해둔 명령어를 사용하기만 하면, 환경 변수 파일은 빌드 전에 항상 자동으로 최신화 될 것이다.

yarn build-android-dev

개선할 점

보안 상으로 문제가 있을까봐 앱 코드 저장소에서 제외시킨 환경변수 파일들을 같은 계정이 관리하는 다른 코드 저장소로 옮겼다. 결국 해당 계정이 뚫리면 환경변수 파일도 같이 뚫리는 거잖아? 라는 생각을 하지 않을 수 없다. 같은 코드 저장소에 있는 것보다야 당연히 낫겠지만, 하나마나한 짓을 한게 아닌가 하는 생각이 들기도 한다.

다음에는 관련 툴(특히 Vault)에 대해 자세히 알아봐야 할 것 같다.

참고 자료