페르소나 4명이 찾은 duplicate key 버그
그날 내가 짠 회차 재정렬 코드가 중간에 실패하면 몇 번을 눌러도 같은 오류가 났다
하루 종일 화면을 고치고, 빌드도 통과하고, 브라우저로 눈으로도 확인했는데, 마지막 점검에서 그날 내가 만든 것이 걸리는 경우가 있다.
방과후 수업 운영 화면이었다. 학교를 추가하는 창을 모달로 바꾸고, 수업일을 달력에서 찍게 만들고, 화면 순서를 다시 잡았다. 돌려보고 나니 어느 정도 마음에 들었다. 다음으로 넘어가기 전에 페르소나를 만들어 마지막으로 개선할 부분이 있는지 보기로 했다.
넷을 병렬로 돌렸다.
- 인수인계받은 담당자 — 화면의 말이 통하나
- 폰으로만 쓰는 사람 — 390px 에서 누를 수 있나
- 손이 미끄러지는 사람 — 되돌릴 수 없는 자리가 어디인가
- 빈 상태 — 아무것도 없을 때 화면이 말이 되나
17건이 나왔다. 그중 네 건이 내가 그날 만든 것이었다.
"중간에 실패하면?"
제일 무거운 건 회차 번호를 다시 매기는 코드였다. 그날 내가 짰다.
번호가 (반, 순번) 으로 유일해야 한다. 그래서 앞에 날짜를 하나 끼울 때 2→3, 1→2 를 바로 쓰면 중간에 부딪힌다. 잠깐 음수로 밀어 두고 최종 번호를 넣게 했다.
seq = -seq - 1 // 1 → -2, 2 → -3, 3 → -4
짤 때 이걸 검증까지 했다. 임시 학교를 만들어 실제 DB 에서 「앞에 하루 끼우기」와 「가운데 하루 빼기」를 돌려 보고 통과를 확인했다. 그러고 유일 제약에 안 걸린다고 적었다.
페르소나가 짚은 건 내가 안 테스트한 경로였다.
"중간에 실패하면?"
재현해 봤다. 절반만 새 번호를 받은 상태를 만들고 다시 저장을 눌렀더니 이렇게 됐다.
망가진 상태: 08-20=1 08-27=2 09-03=-3 10-01=-4
🔴 재시도 실패 — 08-27 (2 → -3): duplicate key ... class_id_seq_key
몇 번을 눌러도 같은 오류가 난다. 밀어 둘 자리와 이미 밀려 있는 자리가 겹쳐서다. 화면엔 「-3회차」로 보이고 순서가 뒤집힌다. 빠져나오려면 그 날짜를 뺐다 다시 넣거나 DB 를 손으로 고쳐야 한다.
밀어 두는 자리를 **「지금 있는 것보다 무조건 아래」**로 바꿨다. 현재 최솟값에서 하나씩 내려가면 어느 시점에도 겹칠 수가 없다. 같은 방식으로 재현했더니 번호가 1, 2, 3, 4 로 정상화됐다.
주석이 가리킨 동작이 곧 버그였다
나머지 셋도 성격이 같았다.
반을 접으면 안 저장한 작업이 통째로 사라졌다. 그런데 그 화면 주석에 내가 "반이 둘이면 일정표를 보며 번갈아 확인하라"고 적어 뒀다. 그 번갈아 보는 동작이 곧 작업을 지우는 동작이었다.
달력에서 날짜를 찍으면 무조건 4교시가 들어갔다. 바로 그 줄에 18:50~20:25 라고 적혀 있었고 그건 계산상 2교시였다.
회차를 지울 때 「적힌 게 있나」를 검사하게 만들어 놓고, 그 검사에서 티어 기록과 수업 기록 네 칸을 빠뜨렸다.
다 고쳤고, 폰에서 44px 미만인 자리가 0건이 됐다.
남는 것
- 내가 만든 것을 내가 다시 봐서는 안 걸린다. 방금 그 코드를 짠 머리가 검토할 때도 같은 가정을 쓴다. 나는 성공 경로를 테스트했고 실패 경로는 떠오르지 않았다 — 안 떠오른 게 아니라, 그 코드를 짤 때 이미 "이러면 되겠지"로 닫아둔 자리라 다시 열 이유가 없었다
- 주석으로 스스로에게 경고까지 해뒀는데도 그 경고가 가리키는 동작이 버그라는 걸 못 봤다. 주석은 「내가 의도한 것」을 적는 자리지 「실제로 일어나는 것」을 재는 자리가 아니다
- 검토자를 나눌 때 효과가 있었던 건 역할이 아니라 「무엇을 보지 말 것인가」를 못 박은 것이었다. 폰 담당에겐 "PC 에서만 나는 문제는 네 일이 아니다 · 숫자로 답해라", 문구 담당에겐 "화면에 안 보이는 것은 지적하지 마라". 그렇게 자르지 않으면 넷이 같은 말을 한다
- 17건을 그대로 옮기지 않은 것도 갈랐다. 실측해 보니 이미 방어가 있는 것도 있었고, 「화면에 이상한 값이 뜬다」류는 전수 확인 결과 0건이었다. 검토자의 말은 확인할 목록이지 결론이 아니다