오늘 운영 DB에서 6개 레코드를 삭제해야 했다. 테스트 데이터가 운영에 섞인 케이스였다.

SQL로 처리하면 한 줄이다. UPDATE income SET deleted_at = NOW() WHERE id IN (...). 빠르고 깔끔하다.

그래서 그렇게 했다.


삭제는 됐다. 데이터도 사라졌다. 그런데 UI에서 이력 버튼을 눌렀더니 아무것도 나오지 않았다.

시스템을 뒤졌다. income_history 테이블이 따로 있었다. API로 삭제하면 내부적으로 _record_income_history()가 호출되어 이력이 남는다. SQL로 직접 건드리면? 아무 기록도 없다. 이력 테이블에 무언가 쌓이는 건 반드시 API를 경유했을 때뿐이었다.

결국 이력 테이블에 수동으로 레코드를 삽입해서 처리했다. SQL로 고치고, 또 SQL로 사후 수습을 했다. 애초에 API를 썼다면 이 과정 전체가 필요 없었다.


이때 깨달은 건 하나다.

API는 단순히 데이터를 읽고 쓰는 창구가 아니다. API는 비즈니스 로직의 집합이다. 이력 기록, 마감 잠금 확인, 유효성 검증 — 이것들이 API 안에 녹아 있다. SQL 직접 실행은 이 레이어 전체를 건너뛴다.

편하게 들어가는 만큼, 시스템이 보장해줬던 것들이 조용히 보장되지 않는다.

더 나쁜 건 이게 티가 잘 안 난다는 점이다. 데이터 자체는 바뀐다. 숫자는 맞다. 이력만 없다. 마감 잠금만 우회됐다. 당장은 아무 문제 없어 보인다. 한참 뒤에, 누군가 이력을 찾거나 롤백이 필요할 때 문제가 드러난다.


이 경험을 절차 문서로 남겼다. 4단계다: 백업 → API 로그인 → curl로 변경 → 이력 확인. SQL이 불가피한 경우에는 이력 테이블 수동 삽입까지 포함해서.

왜 이렇게 복잡하게 해야 하냐고 할 수 있다. 복잡한 게 아니다. 원래 그렇게 해야 하는 거다. SQL 직접 실행이 편해 보였던 것뿐이다.

시스템을 바꾸려면 시스템을 통해야 한다. 그게 우회로가 없는 이유다.