운영 데이터를 안전하게 바꾸기
목표
고객사가 문장으로 보낸 변경 요청을 조건으로 좁히고, 되돌릴 길을 만들고, 사본에서 되돌리기를 시연한 뒤 본 적용과 대조군 검증까지 한 바퀴 끝낼 수 있게 됩니다.
왜 중요한가
앞의 코스들에서 손에 쥔 권한은 사실상 읽기뿐이었습니다. 틀린 조회는 다시 하면 되고, 틀린 가설은 지우고 다시 세우면 됩니다. 쓰기는 다릅니다. 틀린 UPDATE 는 되돌릴 수단을 미리 만들어 둔 만큼만 되돌아갑니다.
이 실습이 강제하는 네 가지는 전부 현장의 규칙입니다. 요청 문장을 조건으로 옮기기 전에 센다 — 요청한 사람이 그린 범위와 실제로 바뀌는 범위는 갈라지고, 그 폭은 세어 보기 전에는 양쪽 다 모릅니다. 되돌릴 길을 먼저 만든다 — 파일 사본은 통째로 되돌리는 용도이고 행 스냅샷은 무엇이 어떻게 바뀌었는지 설명하는 용도라 둘 다 필요합니다. 되돌리기를 말로 두지 않는다 — 되돌리기가 실패하는 이유는 대개 사소한데, 그 사소한 실패를 본 적용 뒤에 발견하면 그때는 사소하지 않습니다. 바뀌지 말아야 할 것을 함께 센다 — 대조군이 없는 검증은 옆을 건드린 사고에서도 통과합니다.
이번 요청은 이렇습니다. 고객사 운영 담당자가 정산 마감을 이유로 "pending 인 주문을 전부 cancelled 로 바꿔 주세요" 라고 보냈습니다. 기준일은 2026-08-29 입니다. 그런데 pending 45건은 한 덩어리가 아닙니다. 오래 멈춘 것도 있고, 어제 들어와 아직 진행 중일 수 있는 것도 있고, 고객이 누구인지 확인되지 않는 것도 있습니다.
이 실습에서 좁히는 잣대는 두 가지입니다 — 생성일이 2026-08-15 이전일 것(그날은 포함하지 않습니다), 그리고 그 주문의 customer_id 가 customers 테이블에 있을 것.
단계
/root/change를 만들고/opt/data/support.sql을/root/change/ops.db로 적재하세요. 고객 원본이 아니라 작업 사본 위에서 시작합니다./root/change/scope.txt에 네 줄을 적으세요.request_total=은 요청대로 옮겼을 때 바뀌는 pending 전체,target=은 위 두 잣대로 좁힌 건수,excluded_recent=는 생성일이2026-08-15이후인 pending,excluded_orphan=은customers에 없는 고객의 오래된 pending 입니다./root/change/ops.db.bak에 파일 사본을 뜨고,/root/change/backup.csv에 대상 행의 변경 전 값을id,customer_id,amount,status,created_at머리글과 함께 뜨세요./root/change/plan.md에 계획서를 쓰세요.## 요청,## 대상과 범위,## 영향,## 되돌리는 방법,## 검증 방법,## 중단 기준여섯 절이 필요하고## 대상과 범위가## 되돌리는 방법보다 앞에 와야 합니다.- 트랜잭션 안에서 UPDATE 를 치고
SELECT changes()를 읽은 뒤 ROLLBACK 하세요./root/change/dryrun.txt에changes=와, 롤백한 뒤 다시 센after_rollback_pending=을 적습니다. /root/change/rehearsal.db를ops.db.bak에서 만들어 같은 변경을 적용하고,backup.csv로 복원하세요./root/change/rehearsal.txt에applied=,restored_pending=, 그리고 복원본과ops.db.bak을 행 단위로 비교한diff=를 적습니다.- 같은 조건으로
ops.db에 본 적용을 하고/root/change/applied.txt에changed=를 남기세요. /root/change/verify.txt에backup_ids_cancelled=,control_pending=,paid_unchanged=,refunded_unchanged=네 줄을 적고,plan.md끝에## 적용 결과절을 덧붙여 검증한 숫자를 남기세요.
참고
sqlite3 -readonly <파일> "<쿼리>"로 읽으면 실수로 쓰는 일을 막을 수 있습니다.- 여러 문장을 한 트랜잭션으로 묶으려면 표준입력으로 넘기세요:
sqlite3 ops.db <<'SQL' ... SQL. - 행 단위 비교는
ATTACH로 두 DB 를 붙인 뒤EXCEPT로 셉니다. - 흔한 실수 1: 2단계에서
excluded_recent와excluded_orphan을 합쳐 세는 것. 제외 사유가 다르면 따로 세야 계획서에 이유를 적을 수 있습니다. - 흔한 실수 2: 5단계에서 ROLLBACK 을 빼먹고 COMMIT 까지 가는 것. 그러면 시험 실행이 본 적용이 됩니다.
- 흔한 실수 3: 6단계의 복원을 UPDATE 문을 새로 짜서 하는 것. 진짜 사고에서 손에 남는 것은 백업 파일뿐이니, 그 파일만으로 되돌아가는지를 봐야 합니다.
작업 사본 만들기
/root/change 를 만들고 /opt/data/support.sql 을 /root/change/ops.db 로 적재하세요. 고객 원본이 아니라 작업 사본 위에서 시작합니다.
고객이 준 스냅샷은 /opt/data/support.sql 입니다. sqlite3 는 SQL 을 표준입력으로 받습니다 — 인자로 주면 빈 DB 만 남습니다.
요청과 실제 범위의 차이 세기
/root/change/scope.txt 에 네 줄을 적으세요. request_total= 은 요청대로 옮겼을 때 바뀌는 pending 전체, target= 은 위 두 잣대로 좁힌 건수, excluded_recent= 는 생성일이 2026-08-15 이후인 pending, excluded_orphan= 은 customers 에 없는 고객의 오래된 pending 입니다.
요청대로 옮긴 조건과 좁힌 조건을 각각 COUNT 로 세세요. 좁히는 잣대는 두 가지입니다 — 생성일과, 그 고객이 customers 에 있는지.
되돌릴 수 있는 상태 만들기
/root/change/ops.db.bak 에 파일 사본을 뜨고, /root/change/backup.csv 에 대상 행의 변경 전 값을 id,customer_id,amount,status,created_at 머리글과 함께 뜨세요.
파일 사본은 cp 로, 행 스냅샷은 sqlite3 의 .mode csv 로 뜹니다. 스냅샷의 조건은 2단계에서 좁힌 그 조건과 같아야 합니다.
변경 계획서 쓰기
/root/change/plan.md 에 계획서를 쓰세요. ## 요청, ## 대상과 범위, ## 영향, ## 되돌리는 방법, ## 검증 방법, ## 중단 기준 여섯 절이 필요하고 ## 대상과 범위 가 ## 되돌리는 방법 보다 앞에 와야 합니다.
여섯 절이 필요합니다. 되돌리는 방법 절에는 무엇으로 되돌리는지 파일 경로까지 적어야 하고, 중단 기준 절은 어떤 결과를 보면 멈출지를 적는 자리입니다.
커밋하지 않고 시험 실행하기
트랜잭션 안에서 UPDATE 를 치고 SELECT changes() 를 읽은 뒤 ROLLBACK 하세요. /root/change/dryrun.txt 에 changes= 와, 롤백한 뒤 다시 센 after_rollback_pending= 을 적습니다.
BEGIN 으로 열고 UPDATE 한 뒤 SELECT changes() 로 영향 행 수를 읽고 ROLLBACK 으로 닫습니다. 롤백했다고 적는 것과 롤백됐다는 것은 다른 사실이니 다시 세세요.
되돌리기 리허설하기
/root/change/rehearsal.db 를 ops.db.bak 에서 만들어 같은 변경을 적용하고, backup.csv 로 복원하세요. /root/change/rehearsal.txt 에 applied=, restored_pending=, 그리고 복원본과 ops.db.bak 을 행 단위로 비교한 diff= 를 적습니다.
운영 사본이 아니라 별도 사본에서 합니다. 되돌릴 때 조건을 다시 짜지 말고 backup.csv 를 읽어 되돌리세요 — 진짜 사고에서 손에 있는 것은 그 파일뿐입니다.
좁힌 조건으로 본 적용하기
같은 조건으로 ops.db 에 본 적용을 하고 /root/change/applied.txt 에 changed= 를 남기세요.
5단계에서 시험한 조건과 문자 그대로 같은 조건을 씁니다. 여기서 조건을 손보고 싶어지면 시험 실행을 다시 해야 한다는 뜻입니다.
대조군까지 세어 확인하기
/root/change/verify.txt 에 backup_ids_cancelled=, control_pending=, paid_unchanged=, refunded_unchanged= 네 줄을 적고, plan.md 끝에 ## 적용 결과 절을 덧붙여 검증한 숫자를 남기세요.
바뀌어야 할 것과 바뀌지 말아야 할 것을 함께 셉니다. 그리고 계획서는 결과를 적어야 닫힙니다 — 적용 결과 절을 덧붙이세요.