운영 데이터 2만 행을 일괄로 바꾸기 전에, 되돌리기를 세 겹으로 해두었습니다
운영 데이터 2만 행을 한 번에 바꿨습니다. 그날 가장 오래 걸린 작업은 무엇을 어떻게 바꿀지 정하는 일이 아니었습니다. 바꾼 것을 되돌리는 방법 세 가지를 먼저 만드는 일이었습니다.
일괄 변경 작업의 품질은 대개 판정 로직이 얼마나 정확한가로 이야기됩니다. 실제로 겁이 나는 지점은 그쪽이 아닙니다. 판정이 조금 틀려도 되돌릴 수 있으면 사고가 아니라 재작업입니다. 판정이 맞아도 되돌릴 수 없으면 확인되지 않은 채로 운영에 남습니다.
사람이 하나씩 열어 고칠 수 있는 규모가 아니었습니다
한 B2B 채권 분석 시스템에서 변제 계획표가 세대별로 쌓입니다. 계좌 하나에 계획표가 여러 세대 있고, 그중 지금 유효한 한 세대만 화면에 보여야 합니다. 운영 데이터에는 어긋남이 두 방향으로 같이 있었습니다.
유효하지 않은 세대가 화면에 그대로 보이는 것
정작 유효한 세대가 지워진 것으로 표시돼 있는 것
조회 대상만 23만 건이었습니다. 운영 담당자가 계좌를 하나씩 열어 눈으로 판정하고 고치는 방식으로는 닿지 않는 규모입니다. 도구를 만들어 한 번에 반영하기로 했고, 그 순간부터 질문이 바뀌었습니다. '얼마나 정확히 고칠 것인가'에서 '틀렸을 때 어떻게 돌아올 것인가'로 옮겨 갔습니다.
검수용 판정을 따로 만들지 않았습니다
판정 규칙 자체는 단순합니다. 계획표 세대를 최신부터 거꾸로 훑어 첫 '시작'을 만나면 그 세대를 유효로 보고, '중단'을 만나면 거기서 멈춥니다. 이 정도면 검수 도구 안에 스무 줄로 다시 짜도 됩니다. 그렇게 하지 않았습니다.
운영에서 조정 이력을 만들 때 쓰는 판정 서비스를 그대로 호출했습니다. 따로 짜면 그 순간부터 판정이 두 벌이 됩니다. 두 벌은 시간이 지나면 갈릴 수 있고, 갈린 뒤에는 어느 쪽이 맞는지 판정하기 어려워집니다. 운영이 쓰는 그 코드를 부르면 검수가 통과한 규칙이 곧 운영 규칙이고, 검수 자체가 회귀 테스트 한 번이 됩니다.
대신 감수한 것도 있습니다. 그 로직이 틀렸으면 검수도 같이 틀립니다. 재사용으로 사는 것은 '이게 정답이다'가 아니라 '두 벌로 갈리지 않는다'입니다. 둘을 같은 것으로 읽으면 나중에 판정 오류를 검수 탓으로 돌리게 됩니다.

바꾼 행마다 같은 표식을 심었습니다
반영은 두 가지 조작이었습니다. 유효 세대가 아닌 것은 보임 여부 칸을 꺼서 감추고, 유효 세대인데 지워져 있던 것은 삭제 표시 칸을 비워 되살립니다. 문제는 그 다음입니다. 바꾼 행을 나중에 다시 찾아낼 수단이 있어야 합니다.
수정 시각 범위로 거르는 방법을 먼저 떠올렸습니다. 같은 시간대에 도는 다른 배치와 섞입니다. 새벽에 돌린 정산 배치가 건드린 행까지 같이 걸리면 되돌리기가 그대로 사고가 됩니다.
그래서 바꾼 행 전부에 같은 값의 작업 표식 컬럼을 심었습니다. 컬럼 하나를 추가하는 비용으로 세 가지를 한꺼번에 얻습니다.
복구 조건 — 이 표식이 붙은 행만 되돌린다
검증 조건 — 이 표식이 붙은 행만 상태를 확인한다
추적 근거 — 몇 달 뒤 '이 행이 왜 이렇게 됐지'에 답할 수 있다
표식 컬럼을 넣을 수 없는 테이블도 있습니다. 스키마를 못 건드리는 외부 시스템이라면 별도 이력 테이블에 바뀐 ID 를 남기는 쪽으로 형태만 바꾸면 됩니다. 핵심은 컬럼이 아니라 '이번 작업이 건드린 것'을 나중에 정확히 다시 집어낼 수 있는가입니다.
되돌리기를 세 겹으로 깔았습니다
문서에 적힌 '문제 생기면 복원'은 복원 방법이 아닙니다. 실행 전에 만들어 둔 SQL 파일과 스냅샷 테이블이 복원입니다. 세 겹을 깐 이유는 각각이 혼자서는 약하기 때문입니다.
겹 | 무엇 | 혼자 두면 약한 지점 |
|---|---|---|
1 | 바꾼 행의 ID 를 직접 나열하는 복구 SQL | 목록 파일을 잃으면 끝난다 |
2 | 작업 표식 값으로 조건을 거는 복구 SQL | 조건이 넓게 걸리면 애먼 행까지 되돌린다 |
3 | 실행 직전 떠 둔 스냅샷 테이블 | 그 사이 정상적으로 들어온 변경까지 같이 덮는다 |
세 겹은 서로의 약점을 가립니다. 첫째 줄(id 직접 나열)이 가장 정밀하고, 셋째 줄(스냅샷)이 가장 넓습니다. 실제로 손이 가는 순서는 정밀한 쪽부터입니다. 스냅샷은 앞의 둘이 모두 소용없을 때만 꺼내는 마지막 수단이고, 꺼내는 순간 그 사이의 정상 변경을 포기한다는 뜻입니다.
되돌리기를 준비했다는 것과 되돌리기가 실제로 동작한다는 것은 다른 이야기입니다. 준비물이 있는데 몇 달째 아무 일도 하지 않고 있는 경우도 있습니다.

검증은 건수가 아니라 기대 상태로 했습니다
실행 결과는 이렇습니다. 조회 대상 230,812건 중 조치 대상이 4,535건으로 좁혀졌고, 판정은 '중단' 2,937건 · '해당 없음' 1,293건 · '시작' 305건으로 갈렸습니다. 실제로 바꾼 행은 감춤 19,184행과 되살림 1,439행을 합쳐 20,623행입니다.
'20,623행이 업데이트됐습니다'는 성공의 증거가 아닙니다. 그 숫자는 SQL 이 돌았다는 사실만 말합니다. 그래서 확인을 판정 결과별 기대 상태로 바꿨습니다.
감춘 행은 전부 안 보임 상태인가
되살린 행은 전부 삭제 표시가 풀렸는가
'중단'·'해당 없음' 판정을 받은 4,230건은 화면에 보이는 계획표가 0개인가
'시작' 판정을 받은 305건은 정확히 1개인가
마지막 두 줄이 핵심입니다. 앞의 두 줄은 내가 시킨 대로 됐는지를 묻고, 뒤의 두 줄은 시킨 것이 맞았는지를 묻습니다. 넷 다 기대값과 맞았습니다. 확인 쿼리의 조건도 전부 작업 표식 기준이었으니, 표식 하나가 복구와 검증 양쪽을 같이 지탱한 셈입니다.
검증을 건수로만 하면 어떻게 되는지는 이미 겪어 봤습니다. 통과한 테스트 수는 사라진 데이터에 대해 아무것도 말해주지 않습니다.
쓰지 않은 되돌리기가 남긴 것
세 겹 중 어느 것도 꺼내지 않았습니다. 준비가 헛수고였다는 뜻은 아닙니다. 준비 없이 돌렸다면 결과가 같았더라도 다음 일괄 작업에서 같은 담력을 또 내야 했을 겁니다. 되돌릴 수 있는 상태로 돌린 경험은 다음 작업의 문턱을 낮춥니다.
같이 정리한 결함도 몇 가지 있습니다. 세대를 묶는 날짜 비교 함수가 월 단위로만 비교해 서로 다른 세대가 한 덩어리로 합쳐지던 문제를 고쳤고, 총회차 값을 다른 출처에서 복사하던 것을 계획표의 마지막 회차에서 가져오도록 바꿨습니다. 정상 계좌의 98.9% 는 값이 그대로였습니다. 파생값까지 비교하느라 같은 계획표가 매주 다시 저장되던 문제도 잡아 재저장 후보가 6건까지 줄었습니다. 벌크 로딩은 1,698초에서 211초로 줄었고 처리량은 초당 135건에서 1,093건이 됐습니다.
남은 위험도 완료 노트에 그대로 적었습니다. 판정상 계획표가 화면에서 사라지는 계좌가 조치 대상의 93% 라 업무 화면 쪽 확인이 필요합니다. 그리고 조회 함수가 '보이는 것만' 가져오게 돼 있어서, 지금 그대로 다시 돌리면 방금 감춘 행들이 조회에서 빠집니다. 재실행 전에 고쳐야 할 자리입니다.
이 마지막 문장을 적어 두는 것이 생각보다 중요합니다. '지금 다시 돌리면 깨지는 이유'는 며칠만 지나면 작업한 사람도 기억하지 못합니다. 다음 담당자는 도구가 한 번 잘 돌았다는 기록만 보고 그대로 다시 실행합니다.

일괄 변경 전에 확인하는 것
규모가 작으면 이 준비는 과합니다. 수십 행짜리 정정에 세 겹을 깔면 준비가 작업보다 커집니다. 갈림선은 '사람이 눈으로 결과를 다 볼 수 있는가'입니다. 그 선을 넘었다면 아래를 순서대로 확인합니다.
판정 로직을 새로 짜지 않고 운영이 쓰는 것을 그대로 부를 수 있는가
바꾼 행을 나중에 정확히 다시 집어낼 수단이 있는가 (표식 컬럼 또는 이력 테이블)
되돌리기가 문서가 아니라 실행 가능한 파일과 테이블로 존재하는가
검증 기준이 변경 건수가 아니라 판정 결과별 기대 상태로 적혀 있는가
지금 그대로 다시 돌리면 깨지는 이유가 완료 노트에 적혀 있는가
다섯 줄 중 앞의 셋은 실행 전에, 넷째는 실행 직후에, 다섯째는 작업을 닫을 때 확인합니다. 셋째 줄이 가장 자주 비어 있습니다. 회의록에 '롤백 플랜 수립'이라고 적히고 실제 파일은 없는 상태가 흔합니다.
마무리
운영 데이터를 일괄로 바꾸는 작업에서 코드는 생각보다 짧습니다. 길어지는 쪽은 준비입니다. 판정을 어디서 가져올지, 바꾼 행을 어떻게 다시 찾을지, 되돌리기를 무엇으로 남길지 세 가지를 정하고 나면 실행 자체는 한 번에 끝납니다.
그중 하나만 고르라면 표식 컬럼입니다. 컬럼 하나가 복구 조건이 되고 검증 조건이 되고 나중에 답을 찾는 근거가 됩니다. 2만 행을 바꾸는 날 가장 싸게 산 보험이었습니다.