CI 배포 파이프라인에서 광고 빌드 실패 시 서버 폴더 전체 삭제를 막는
목차
CI 파이프라인에서 rsync --delete 옵션이 빈 dist 폴더를 바라보면서 서버의 게임 폴더를 통째로 날릴 뻔한 사고를 막은 가드 로직을 추가했다.
배경: rsync --delete 의 무서움
rsync --delete 는 강력하다. 소스에 없는 파일을 타깃에서 삭제해주기 때문에 "클린한 배포"를 만들기 딱 좋은 옵션이다. 그런데 그 강력함이 양날의 검이다.
소스 디렉터리가 비어 있을 때도 rsync 는 그냥 실행된다. 에러도 없고 경고도 없다. 타깃 서버 입장에서는 "소스에 아무것도 없으니 다 지워야지" 라고 충실하게 동작할 뿐이다. 빌드 단계가 실패하거나, 아웃풋 경로가 잘못 잡히거나, 빌드 스크립트가 아무것도 만들지 않고 exit 0 으로 끝난 경우—이 세 가지 상황 모두 dist 폴더가 비어있는 채로 rsync 까지 도달하게 만든다.
이번 케이스가 딱 그 상황이었다. npm run build:ads 라는 npm 스크립트가 내부적으로 경로 문제가 생기면서 dist 를 제대로 채우지 못했고, CI 는 exit code 를 0으로 반환하며 다음 단계로 넘어갔다. rsync 는 빈 폴더를 충실히 서버에 동기화했을 것이고, 그 결과는 서버 게임 폴더 전체 삭제였을 것이다.
변경 내용
deploy.yml 에 두 가지를 동시에 수정했다.
| 항목 | 변경 전 | 변경 후 |
|---|---|---|
| 빌드 커맨드 | npm run build:ads |
node build-ads.js |
| dist 비어있으면 | rsync 그대로 실행 → 서버 폴더 삭제 | 파이프라인 fail 로 조기 중단 |
빌드 커맨드 변경은 단순해 보이지만 핵심이다. npm run build:ads 는 package.json 의 scripts 를 경유하기 때문에 실행 경로, 환경변수, npm 스크립트 레이어에서 조용히 실패할 여지가 있었다. node build-ads.js 로 직접 호출하면 스크립트 레이어를 제거하고, 실패 시 exit code 도 더 명확하게 올라온다.
가드 로직은 이렇게 달았다:
- name: Check dist is not empty
run: |
if [ -z "$(ls -A dist/)" ]; then
echo "❌ dist/ is empty. Build may have failed. Aborting deploy."
exit 1
fi
rsync 스텝 바로 앞에 배치했다. dist 가 비어 있으면 파이프라인이 즉시 fail 되고, 이후 rsync 는 실행조차 안 된다.
회고
사실 이 가드는 "있으면 좋은 것"이 아니라 처음부터 있었어야 하는 것이었다. rsync --delete 를 쓰는 순간, 항상 소스 디렉터리가 유효한지 검증하는 스텝이 짝으로 따라붙어야 한다. 이건 CI 설계의 기본 원칙인데, 초기에 빠르게 파이프라인을 구성하면서 그냥 넘어간 게 화근이었다.
팀에 공유하면서 몇 가지를 같이 얘기했다:
--delete옵션은 "소스가 항상 신뢰할 수 있는 상태" 일 때만 쓸 것- 빌드 아웃풋 경로를 명시적으로 검증하는 스텝은 rsync/scp/s3 sync 어떤 배포 방식이든 표준으로 달아둘 것
- npm scripts 를 CI 에서 쓸 때는 exit code 전파 여부를 반드시 확인할 것.
npm run은 생각보다 에러를 삼키는 경우가 있다
이번엔 실제로 배포가 실행되기 전에 잡았지만, 타이밍이 조금만 달랐으면 서버 복구 작업에 꽤 많은 시간을 쏟았을 거다. CI 파이프라인은 "빠르게 배포하는 도구"이기 전에 "잘못된 배포를 막는 도구"라는 걸 다시 한 번 체감했다.
.github/workflows/deploy.yml 한 파일 수정이지만, 이 커밋이 막아낸 시나리오의 무게는 꽤 무겁다. 파이프라인에 --delete 계열 옵션이 붙어 있다면 오늘 한 번 점검해볼 것을 권한다.
끝.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.