PostgreSQL로 전환한 뒤 첫 운영 배포. "잘 돌아가겠지"라는 생각으로 점검을 시작했는데, 예상보다 많은 구멍이 나왔다.
1. 테스트 데이터가 운영에 있었다
운영 DB에 2026년 3월 날짜의 수입 레코드 6건이 있었다. 아직 2월인데. batch_id가 20260301, 20260315 — 누가 봐도 테스트 데이터다. 홍길동, 박영희 같은 이름이 섞여 있었다.
id=4450 | 2026-03-01 | 박영희 | 100,000 | batch=20260301
id=4451 | 2026-03-15 | 홍길동,김영희 | 500,000 | batch=20260315
운영 DB에 직접 테스트를 돌린 흔적이다. DELETE로 즉시 정리했다. 교훈: 운영 DB에서 테스트하지 말 것. 당연한 건데 급하면 잊는다.
2. 마이그레이션이 빠져 있었다
is_deleted 컬럼을 조회하니 "column does not exist" 에러. 소프트 삭제 마이그레이션(002_soft_delete.sql)이 운영에 적용되지 않았다.
원인은 단순했다. 로컬에서 개발만 하고 커밋을 안 했다. 커밋이 안 됐으니 배포도 안 됐고, 배포가 안 됐으니 마이그레이션도 안 돌았다.
다행히 배포된 코드에서 is_deleted를 아직 참조하지 않아서 당장 오류는 없었다. 하지만 다음 배포에서 터질 시한폭탄이었다.
조치: 18개 파일 일괄 커밋 → 배포 → 마이그레이션 자동 실행. deploy.sh가 마이그레이션 단계를 포함하고 있어서 배포만 하면 해결됐다.
3. 백업이 SQLite 파일이었다
Drive에 올라가 있는 DB 백업 파일들 — finance_20260208.db, finance_20260215.db — 전부 SQLite 파일이었다. PostgreSQL로 전환한 이상 이건 의미가 없다.
그리고 운영 서버(VM)에는 백업 디렉토리 자체가 없었다. 앱 컨테이너에 pg_dump도 설치되어 있지 않았다.
조치 3가지:
- Dockerfile에
postgresql-client추가 →pg_dump사용 가능 docker-compose.prod.yml에 볼륨 마운트:./backups → /app/scripts/db/backupsclose_offering --backup-db를pg_dumpSQL 덤프 방식으로 전환
# docker-compose.prod.yml
volumes:
- ./backups:/app/scripts/db/backups
이제 마감 시 자동으로 pg_dump → .sql → Drive 업로드가 된다.
4. 복원에 안전장치가 없었다
기존 복원 API는 confirm=true 파라미터 하나뿐이었다. 실수로 클릭하면 DB가 통째로 교체된다.
3단계 안전장치를 추가했다:
| 단계 | 내용 |
|---|---|
| 미리보기 | 현재 DB vs 백업 파일의 테이블별 레코드 수 비교 |
| 비밀번호 | 관리자 비밀번호 재입력 |
| 쿨다운 | 5초 카운트다운, 취소 가능 |
백업 파일의 레코드 수는 pg_dump의 COPY 블록을 regex로 파싱해서 추출한다. 별도 DB 연결 없이 파일만 읽으면 된다.
pattern = rf"COPY public\.{table}\s[^\n]*FROM stdin;\n(.*?)\n\\."
match = re.search(pattern, content, re.DOTALL)
5. Docker 빌드와 로컬 빌드는 다르다
로컬에서 npx tsc --noEmit 통과해도 Docker 빌드에서 TS 에러가 난다. 이번에 2번 겪었다.
onEdit?: undefined— optional prop에 undefined 전달 (strict mode)- 미사용 변수
accentColor—noUnusedLocals차이
Docker의 tsc -b가 더 엄격하다. 로컬 통과를 맹신하면 안 된다.
배포 전 체크리스트
이번 경험으로 만든 운영 배포 전 확인 항목:
- 커밋되지 않은 변경사항 확인 (
git status) - 마이그레이션 파일이 커밋에 포함되었는가
- 운영 DB에 테스트 데이터가 없는가
- 백업이 현재 DB 엔진(PostgreSQL)과 호환되는가
- 볼륨 마운트로 영속 데이터가 보호되는가
- Docker 빌드가 로컬과 동일하게 통과하는가
오늘의 숫자
- 커밋 5개, 배포 5회
- 테스트 데이터 6건 삭제
- 운영 DB: 414 income / 110 expense
- 백업:
finance_20260219.sql(86KB, pg_dump)