TT Lab
시작하기
배우기 러닝패스 코스

되돌릴 수 없는 변경

운영 데이터를 안전하게 바꾸기

TT Lab 에서 이어서 보기

목표

고객사가 문장으로 보낸 변경 요청을 조건으로 좁히고, 되돌릴 길을 만들고, 사본에서 되돌리기를 시연한 뒤 본 적용과 대조군 검증까지 한 바퀴 끝낼 수 있게 됩니다.

왜 중요한가

앞의 코스들에서 손에 쥔 권한은 사실상 읽기뿐이었습니다. 틀린 조회는 다시 하면 되고, 틀린 가설은 지우고 다시 세우면 됩니다. 쓰기는 다릅니다. 틀린 UPDATE 는 되돌릴 수단을 미리 만들어 둔 만큼만 되돌아갑니다.

이 실습이 강제하는 네 가지는 전부 현장의 규칙입니다. 요청 문장을 조건으로 옮기기 전에 센다 — 요청한 사람이 그린 범위와 실제로 바뀌는 범위는 갈라지고, 그 폭은 세어 보기 전에는 양쪽 다 모릅니다. 되돌릴 길을 먼저 만든다 — 파일 사본은 통째로 되돌리는 용도이고 행 스냅샷은 무엇이 어떻게 바뀌었는지 설명하는 용도라 둘 다 필요합니다. 되돌리기를 말로 두지 않는다 — 되돌리기가 실패하는 이유는 대개 사소한데, 그 사소한 실패를 본 적용 뒤에 발견하면 그때는 사소하지 않습니다. 바뀌지 말아야 할 것을 함께 센다 — 대조군이 없는 검증은 옆을 건드린 사고에서도 통과합니다.

이번 요청은 이렇습니다. 고객사 운영 담당자가 정산 마감을 이유로 "pending 인 주문을 전부 cancelled 로 바꿔 주세요" 라고 보냈습니다. 기준일은 2026-08-29 입니다. 그런데 pending 45건은 한 덩어리가 아닙니다. 오래 멈춘 것도 있고, 어제 들어와 아직 진행 중일 수 있는 것도 있고, 고객이 누구인지 확인되지 않는 것도 있습니다.

이 실습에서 좁히는 잣대는 두 가지입니다 — 생성일이 2026-08-15 이전일 것(그날은 포함하지 않습니다), 그리고 그 주문의 customer_id 가 customers 테이블에 있을 것.

단계

  1. /root/change 를 만들고 /opt/data/support.sql 을 /root/change/ops.db 로 적재하세요. 고객 원본이 아니라 작업 사본 위에서 시작합니다.
  2. /root/change/scope.txt 에 네 줄을 적으세요. request_total= 은 요청대로 옮겼을 때 바뀌는 pending 전체, target= 은 위 두 잣대로 좁힌 건수, excluded_recent= 는 생성일이 2026-08-15 이후인 pending, excluded_orphan= 은 customers 에 없는 고객의 오래된 pending 입니다.
  3. /root/change/ops.db.bak 에 파일 사본을 뜨고, /root/change/backup.csv 에 대상 행의 변경 전 값을 id,customer_id,amount,status,created_at 머리글과 함께 뜨세요.
  4. /root/change/plan.md 에 계획서를 쓰세요. ## 요청, ## 대상과 범위, ## 영향, ## 되돌리는 방법, ## 검증 방법, ## 중단 기준 여섯 절이 필요하고 ## 대상과 범위 가 ## 되돌리는 방법 보다 앞에 와야 합니다.
  5. 트랜잭션 안에서 UPDATE 를 치고 SELECT changes() 를 읽은 뒤 ROLLBACK 하세요. /root/change/dryrun.txt 에 changes= 와, 롤백한 뒤 다시 센 after_rollback_pending= 을 적습니다.
  6. /root/change/rehearsal.db 를 ops.db.bak 에서 만들어 같은 변경을 적용하고, backup.csv 로 복원하세요. /root/change/rehearsal.txt 에 applied=, restored_pending=, 그리고 복원본과 ops.db.bak 을 행 단위로 비교한 diff= 를 적습니다.
  7. 같은 조건으로 ops.db 에 본 적용을 하고 /root/change/applied.txt 에 changed= 를 남기세요.
  8. /root/change/verify.txt 에 backup_ids_cancelled=, control_pending=, paid_unchanged=, refunded_unchanged= 네 줄을 적고, plan.md 끝에 ## 적용 결과 절을 덧붙여 검증한 숫자를 남기세요.

참고

작업 사본 만들기

/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 끝에 ## 적용 결과 절을 덧붙여 검증한 숫자를 남기세요.

바뀌어야 할 것과 바뀌지 말아야 할 것을 함께 셉니다. 그리고 계획서는 결과를 적어야 닫힙니다 — 적용 결과 절을 덧붙이세요.