2026년 6월 26일 | 개발 깨알 상식_Tips
서버 동기화는 실패했을 때보다 성공했는데 많이 지웠을 때 더 당황스럽다. 로컬 build 폴더를 운영 서버나 스테이징 서버로 밀어 넣는 일은 손에 익으면 거의 반사적으로 치게 되는데, 그 순간 `--delete`가 같이 붙어 있으면 이야기가 달라진다. 복사는 빠르게 끝났고 명령도 에러 없이 끝났는데, 나중에 보니 서버 쪽에만 있던 파일이 조용히 사라지는 식이다.
복사 전에 변경 계획으로 보기
나는 rsync를 쓸 때 명령을 두 단계로 나누는 편이 낫다고 본다. 첫 단계는 파일을 옮기기보다 변경 계획을 출력하는 명령으로 시작한다. `-n` 또는 `--dry-run`은 실제 쓰기를 하지 않고, `-i` 또는 `--itemize-changes`는 어떤 파일이 생기고 바뀌고 지워질지 짧은 코드로 보여 준다.
rsync -ani --delete ./build/ deploy:/srv/app/
여기서 중요한 건 `-a`보다 `-n`과 `-i`다. archive 옵션은 익숙해서 자주 붙지만, dry-run과 itemize가 없으면 내가 실행 전에 읽을 수 있는 근거가 줄어든다. 특히 `--delete`가 들어간 명령은 단순 복사를 넘어 대상 폴더를 원본 모양으로 맞추는 작업에 가깝다. 원본에 없는 파일은 대상에서도 사라질 수 있다는 뜻이다.
삭제 신호는 한 줄만 보여도 멈춘다
itemize 출력에서 제일 먼저 찾는 건 `*deleting`이다. 이 줄이 보이면 나는 보통 바로 실행 명령으로 넘어가지 않는다. 삭제가 의도된 것인지, 원본 경로를 한 단계 잘못 잡은 것인지, 배포 산출물에는 없지만 서버에 남아 있어야 하는 업로드 파일인지부터 본다.
*deleting uploads/old-report.csv
>f..t...... assets/app.js
>f+++++++++ assets/new-style.css
`>f`는 로컬에서 대상 쪽으로 파일이 간다는 신호로 읽을 수 있고, `+++++++++`는 새 파일에 가깝다. 반대로 삭제 줄은 방향 코드보다 더 크게 봐야 한다. 파일 하나라도 `uploads`, `cache`, `shared`, `media`처럼 서버 쪽 상태가 섞일 수 있는 폴더에 걸리면, 그 명령은 배포 명령을 넘어 데이터 정리 명령이 되어 버린다.
경로의 슬래시가 결과를 바꾼다
rsync에서 source 뒤의 슬래시는 은근히 큰 차이를 만든다. `./build/`는 build 안쪽 내용을 대상으로 맞추는 느낌이고, `./build`는 build 폴더 자체가 들어가는 모양에 가까워진다. 이 차이를 기억으로만 처리하면 언젠가 헷갈린다. 그래서 dry-run 출력에서 최상위 경로가 내가 예상한 모양인지 먼저 본다.
내 기준은 단순하다. 출력 첫 20줄 안에서 경로 prefix가 예상과 다르면 멈춘다. 삭제 줄이 없더라도 멈춘다. 내가 바꾸려던 파일이 `assets/` 아래였는데 출력이 `build/assets/`로 시작하거나, 반대로 `assets/` 대신 루트 파일들이 우르르 보이면 명령 문자열보다 출력 쪽이 더 믿을 만하다.
대상 서버에만 남아야 하는 예외 폴더가 있다면, 그 이름도 머리로만 기억하지 않는다. `--exclude-from` 같은 파일로 빼 두고, dry-run 결과에서 그 폴더가 정말 빠졌는지 확인한다. 예외 목록이 문서가 아니라 실행 입력으로 들어가야 다음 배포에서도 같은 조건을 다시 쓸 수 있다.
로그 파일을 남겨 두는 이유
dry-run은 화면에서 한 번 보고 지나가면 효과가 반쪽이다. 배포 직전에는 마음이 급해서 사람이 보고 싶은 줄만 보게 된다. 나는 삭제가 있는 명령일수록 출력 결과를 파일로 남겨 두는 쪽이 편하다. 나중에 문제가 생겼을 때 실제 실행 로그와 계획 로그를 비교할 수 있기 때문이다.
rsync -ani --delete ./build/ deploy:/srv/app/ | tee rsync-plan.txt
그다음에는 `rsync-plan.txt`에서 `*deleting`과 예상 밖 상위 폴더를 먼저 찾는다. 규모가 크면 새 파일 수, 삭제 파일 수, 바뀐 파일 수를 따로 세도 좋다. 이 과정은 거창한 배포 시스템이 없어도 쓸 수 있고, 작은 VPS나 사내 테스트 서버에서도 바로 손에 잡힌다.
자동화에 넣을 때의 멈춤 조건
이 패턴을 스크립트에 넣을 때도 완전 자동 실행으로 바로 넘기지는 않는다. 적어도 삭제 줄이 하나 이상 있으면 사람이 보는 단계로 멈추게 만든다. 단순한 성공/실패 종료 코드와 별도로, 성공한 dry-run 안에서도 위험 신호를 따로 보는 식이다.
grep -n "*deleting" rsync-plan.txt
여기서 grep 결과가 비어 있으면 바로 안전하다는 뜻은 아니다. 다만 내가 반드시 확인해야 할 첫 번째 표면은 줄어든다. 그 다음에는 새 파일 수가 갑자기 많아졌는지, 대상 경로가 의도한 prefix로 시작하는지, 서버에만 있어야 하는 폴더가 계획 안에 들어오지 않았는지를 본다. 이 정도만 해도 실수의 모양이 꽤 선명해진다.
동료에게 확인을 부탁할 때도 명령 전체를 던지는 것보다 계획 파일의 세 줄을 보여 주는 편이 낫다. 삭제 후보, 새로 들어갈 최상위 폴더, 예외로 남긴 폴더를 나란히 보여 주면 상대도 경로 해석에 바로 들어올 수 있다. 복잡한 설명보다 실제 dry-run 출력이 더 빠른 리뷰 자료가 된다.
작은 팀에서는 이 파일이 배포 승인 메모 역할도 한다. 누가 언제 어떤 계획을 보고 실행했는지 남아 있으면, 나중에 장애가 났을 때 기억을 되짚는 시간이 줄어든다. 특히 금요일 오후처럼 서둘러 닫고 싶은 시간대에는 이 작은 기록이 꽤 큰 안전장치가 된다. 삭제가 실제로 없었다는 확인도 기록으로 남기면 다음 배포 때 기준선이 된다.
실행 명령은 계획 명령에서 한 글자만 줄인다
검토가 끝난 뒤 실제 실행할 때는 dry-run 명령에서 `n`만 빼는 식으로 간격을 줄인다. 새 명령을 다시 손으로 조합하면 그 사이에 다른 옵션이 들어가거나 경로가 바뀔 수 있다. 계획을 본 명령과 실행 명령이 최대한 같은 모양이어야, 내가 확인한 표면과 실제 변경 표면이 맞는다.
rsync -ai --delete ./build/ deploy:/srv/app/
작은 팁처럼 보이지만, 이 습관은 꽤 많은 사고를 줄여 준다. rsync는 좋은 도구지만 친절하게 멈춰 주는 도구는 아니다. 그래서 나는 빠른 동기화보다, 실행 직전에 삭제와 덮어쓰기가 눈에 보이는 상태를 더 믿는다.
'[개발 깨알 상식_Tips]' 카테고리의 다른 글
| 체크섬 manifest로 잡는 깨진 다운로드 (0) | 2026.07.01 |
|---|---|
| 첫 실패 기준으로 줄인 pytest 로그 (0) | 2026.06.30 |
| 느린 API 호출의 연결 시간과 응답 시간 분리 (0) | 2026.06.25 |
| SQLite 백업에서 빠진 WAL 로그 (0) | 2026.06.24 |
| HTTP 429와 Retry-After 헤더 (0) | 2026.06.23 |