목차
배포에는 되돌리기 버튼이 있다. 스키마 변경에는 버튼이 있어도 안 눌리는 것들이 있다.
되돌리기 버튼이 못 하는 일
새 버전을 올렸는데 문제가 생겼다. 되돌리기를 누른다. 코드는 몇 분 만에 옛 버전으로 돌아간다.
그런데 그 배포에 DROP COLUMN이 하나 끼어 있었다면, 코드가 돌아간 자리에 그 컬럼을 찾는 옛 코드가 서 있게 된다. 컬럼은 없다. 되돌리기를 했는데 더 나빠졌다.
가게에서 벽을 헐고 인테리어를 바꿨다가 마음에 안 들어 원래대로 되돌린다고 하자. 가구는 제자리로 옮기면 된다. 헌 벽은 다시 세울 수 있지만 그 벽에 있던 것들은 안 돌아온다.
어디까지 되돌아가나
한 변경을 여러 배포로 쪼갤 때 실제로 무엇을 사는지가 여기서 보인다.
추가안 쓰는 컬럼
쓴다새 쪽을 읽는다
쓰기 중단값은 그대로 있다
삭제값이 사라진다
여기까지는 어느 지점에서든 멈추고 돌아온다잃은 것이 없다 - 값이 하나 더 있을 뿐이다
뒤가 없는 문 하나가장 나중, 가장 안 급함
쪼개기가 위험을 없앤 게 아니다. 한 방향인 문을 하나로 줄이고 그 앞을 전부 되돌아갈 수 있게 만든 것이다.
앞 단계들은 전부 양방향이다. 새 컬럼만 만들어 둔 상태는 언제든 되돌릴 수 있고, 양쪽에 쓰는 상태도 옛 컬럼만 읽는 상태로 돌아갈 수 있다. 백필을 다 한 상태도 그렇다 - 값이 하나 더 있을 뿐 잃은 게 없다.
한 방향인 곳은 마지막 하나다. 옛 컬럼을 지우는 순간부터 뒤가 없다.
이게 목표다. 쪼개기가 위험을 없앤 게 아니다. 한 곳으로 몰아두고, 거기까지 가는 동안 언제든 멈출 수 있게 만든 것이다. 그리고 그 한 곳은 가장 나중에 오고, 가장 급하지 않다.
지우는 것만 위험한 게 아니다
DROP이 눈에 띌 뿐, 정보를 잃는 변경은 더 있다. 문장만 봐서는 안 위험해 보이는 것들이라 더 조심해야 한다.
| 변경 | 무엇을 잃나 |
|---|---|
VARCHAR(100) → VARCHAR(50) | 51자 이상이던 값의 뒷부분 |
BIGINT → INT | 범위를 넘는 값 |
DECIMAL(10,4) → DECIMAL(10,2) | 소수점 아래 자릿수 |
| 문자셋 축소 | 표현 못 하는 글자 |
크기를 늘리는 건 대개 안전하고 줄이는 건 대개 위험하다. 늘릴 때는 기존 값이 전부 새 범위 안에 들어가지만, 줄일 때는 안 들어가는 값이 있을 수 있어서다.
그리고 방향이 다른 위험이 하나 더 있다. 제약을 새로 거는 변경은 데이터를 안 잃는 대신 실패한다. NOT NULL이나 UNIQUE를 걸었는데 어기는 행이 하나라도 있으면 변경 자체가 안 된다. 5천만 행을 다 검사한 뒤에 실패하는 경우도 있어서, 거는 데 걸린 시간을 통째로 버린다. 걸기 전에 어기는 행이 0인지 세어 보는 게 순서다.
그래서 지우기를 미룬다
한 방향 지점이 마지막에 있다는 건, 거기 안 가고 버틸 수 있다는 뜻이기도 하다. 옛 컬럼을 안 지우고 그냥 두면 어떻게 되나.
대개는 아무 일도 안 일어난다. 안 읽는 컬럼 하나가 남아 있을 뿐이다. 그래서 실무에서는 지우기를 몇 주에서 몇 달 미룬다. 되돌릴 일이 없다는 확신이 설 때까지.
다만 공짜는 아니다.
- 스키마가 지저분해진다. 새로 온 사람이
nickname과display_name중 뭘 써야 하는지 모른다. - 저장 공간을 계속 먹는다. 큰 테이블이면 무시 못 할 크기가 된다.
- 잊힌다. 지우기로 한 걸 안 지우면 1년 뒤엔 아무도 그게 왜 있는지 모른다. 그리고 그때는 지워도 되는지 판단할 사람이 없다.
세 번째가 진짜 비용이다. 미루는 건 좋은 판단인데 미루기로 한 걸 어딘가에 적어 두지 않으면 미루는 게 아니라 방치가 된다.
지우기 전에 확인하는 것
정말 아무도 안 읽는지는 코드를 뒤져서 알기 어렵다. 동적으로 만든 쿼리, 리포트 도구, 배치 스크립트, 다른 팀의 조회가 있다.
그래서 코드가 아니라 DB에게 물어본다. 그 컬럼을 참조한 쿼리가 최근에 있었는지 쿼리 로그나 통계에서 확인한다. 코드 검색은 우리 저장소만 보지만 DB는 누가 읽든 다 본다.
관측 기간이 짧으면 거짓말이 된다. 한 달에 한 번 도는 월말 정산이 그 컬럼을 읽고 있으면 2주 관측으로는 안 잡힌다. 분기 마감이면 더하다. 확인 기간은 그 테이블을 쓰는 가장 긴 주기보다 길어야 한다.
이름만 바꿔 두는 방법
지우기 전에 한 단계를 더 두는 방법이 있다. 이름을 바꿔 두는 것이다. nickname을 nickname_deprecated_20251203처럼 바꾸면, 그 컬럼을 아직 읽는 코드가 있을 때 에러가 난다.
일부러 깨뜨리는 것이다. 조용히 잘못된 값을 읽는 것보다 시끄럽게 실패하는 게 낫고, 이름 바꾸기는 되돌릴 수 있다. 문제가 터지면 이름을 되돌리면 되고 데이터는 그대로다.
함정도 있다. 이름 바꾸기가 테이블을 다시 쓰는 제품도 있고, SELECT *로 조회하는 코드는 컬럼 이름이 바뀌어도 안 깨진 채 이상한 값을 받아간다. 그리고 이름을 바꿔 놓고 잊으면 앞의 세 번째 비용이 그대로 온다 - 이제 이름까지 이상한 컬럼이 남는다.
정말 지울 때
미루고 확인하고 이름까지 바꿔 봤으면 이제 지운다. 이 순간에 지켜야 할 것은 두 가지다.
- 지우기만 하는 배포로 만든다. 다른 변경을 같이 싣지 않는다. 문제가 생겼을 때 원인이 하나여야 한다.
- 되돌릴 수 없다는 걸 인정하고 시작한다. 백업이 있어도 그건 되돌리기가 아니다 - 백업으로 돌아가면 그 뒤에 들어온 거래까지 같이 사라진다.
값이 정말 아까우면 지우기 전에 다른 테이블로 옮겨 둔다. 지우는 대신 옮기면 한 방향 지점이 한 번 더 미뤄진다. 물론 그 테이블도 언젠가는 지워야 하지만, 급할 때 내리는 결정을 급하지 않을 때로 옮기는 것만으로 값이 있다.
여기서 비유가 깨진다
가게에서 헐어버린 벽은 헐린 게 눈에 보인다. 다음 날 출근하면 벽이 없다.
컬럼을 지우는 건 안 보인다. 지워도 대부분의 화면은 멀쩡히 돌아가고, 몇 주 뒤 월말 정산이 돌 때 처음 드러난다. 한 방향 문을 통과한 시점과 통과한 걸 알게 되는 시점이 멀리 떨어져 있다. 그래서 이 세계에서는 벽을 헐기 전에 눈을 대신할 것을 마련해야 한다 - 그게 위에서 본 관측이고 이름 바꾸기다.
실무에서: 한 방향 지점을 세어 본다
계획을 검토할 때 물어볼 것은 하나다. “이 계획에 한 방향 지점이 몇 개인가.”
- 0개면 언제든 멈출 수 있다. 대부분의 단계가 여기 있어야 한다.
- 1개면 정상이다. 마지막에 있고, 급하지 않고, 앞에서 충분히 확인했으면 된다.
- 2개 이상이면 순서를 다시 짠다. 특히 한 배포에 두 개가 있으면 거의 항상 쪼갤 수 있다.
가장 흔한 사고는 순서를 쪼갠 계획을 만들어 놓고 **“어차피 잘 될 테니 1차와 5차를 같이 하자”**로 합치는 것이다. 합치는 순간 되돌리기 버튼이 사라진다. 쪼개기의 값은 단계가 많다는 데 있는 게 아니라 멈출 수 있는 지점이 많다는 데 있다.
정리
- 되돌리기 버튼은 코드만 되돌린다. 지운 데이터는 안 돌아온다.
- 한 방향 지점은 마지막 하나여야 한다. 쪼개기는 위험을 없애는 게 아니라 한 곳으로 몬다.
DROP만 위험한 게 아니다. 크기를 줄이는 변경은 값을 자르고, 제약을 거는 변경은 실패한다.- 지우기는 미루되 미룬다는 걸 적어 둔다. 안 적으면 방치가 된다.
- 안 읽는지는 코드가 아니라 DB에게 묻고, 관측 기간은 가장 긴 주기보다 길어야 한다.