운영 중에 제보가 두 건 올라왔다.
하나, 올린 것과 다른 파일이 받아진다. 둘, 받기를 눌러도 파일이 제대로 안 받아진다.
둘 다 데이터가 잘못 나가는 쪽이라 급했다. 바로 코드를 따라갔다.
업로드하는 화면부터 시작했다. 파일을 올리고 그 응답을 받아 다음 요청에 넘기는 구간 — 순서대로 기다리고 있어서 경합이 생길 구조가 아니었다. 서버 쪽 저장 로직도 봤다. 파일마다 고유한 이름을 새로 만들어 저장하고 있어서 덮어쓰기가 구조적으로 불가능했다. 조회할 때도 해당 건에 속한 파일만, 지정된 순서대로 읽고 있었다. 다운로드 시점에는 그 파일이 정말 그 건의 것인지 한 번 더 검증하고 있었다.
화면이 기대하는 응답 형태와 서버가 주는 형태도 필드 단위로 맞춰봤다. 정확히 일치했다.
여섯 군데를 훑고 나온 결론은 하나였다. 정적으로 잡히는 버그가 없다.
이때가 제일 위험한 순간이다. 원인은 모르겠는데 뭔가는 해야 할 것 같으니까, 짐작으로 한 줄 바꾸고 배포하고 물어보게 된다. "이제 되나요?" 그게 아니라고 하면 또 한 줄 바꾼다. 이걸 몇 번 반복하면 코드에는 근거 없는 방어 코드가 쌓이고, 원인은 그대로 남는다.
그래서 고치는 걸 멈췄다.
두 증상 다 실제 나간 데이터를 봐야 확정할 수 있는 종류였다. 그런데 그걸 볼 방법이 없었다. 나간 뒤에는 아무것도 남지 않았고, 화면에서 다시 열어볼 수도 없었다.
그래서 진단 도구부터 만들었다. 무엇이 나갔는지, 어떤 파일이 붙었는지를 나중에 그대로 꺼내볼 수 있는 화면이다.
설계에서 한 가지를 정했다. 나갈 때의 내용을 그 시점에 통째로 저장한다. 나중에 다시 조립하지 않는다. 템플릿이 바뀌면 과거에 나간 문장도 같이 바뀌어 보일 텐데, 그러면 "그때 정확히 뭐가 나갔나"를 영영 못 본다. 지금 필요한 건 재현이 아니라 증거였다.
만들고 나니 화면 하나가 생겼다. 실제로 나간 문장, 그때 채워진 값들, 붙은 파일 목록, 그리고 그 파일을 바로 열어보는 버튼.
그 화면으로 실제 데이터를 열자 한 건은 금방 정리됐다.
파일은 멀쩡했다. 서버도 정확한 응답을 주고 있었다. 문제는 어디서 여느냐였다.
안내 메시지의 버튼을 누르면 메신저 앱 안에서 브라우저가 열린다. 앱 바깥의 브라우저가 아니라 앱이 품고 있는 브라우저다. 그 안에서는 화면이 파일을 메모리에 만들어놓고 내려받게 하는 방식이 동작하지 않는다. 우리 화면이 정확히 그 방식이었다.
그래서 그 환경에서만 다른 경로로 바꿨다. 서버가 이미 "이건 내려받는 파일이다"라고 알려주는 응답을 주고 있었으니, 화면은 그 주소로 그냥 이동만 하면 됐다. 백엔드는 손댈 게 없었다.
정적 분석으로 절대 안 잡히는 종류였다. 코드는 처음부터 옳았고, 옳은 코드가 특정 실행 환경에서 안 돌았을 뿐이다.
다른 한 건은 지금도 재현이 안 된다.
다만 코드를 훑을 때 딱 한 군데 마음에 걸린 곳이 있었다. 화면에서 파일을 순번으로 식별하고 있었다. 첫 번째, 두 번째, 세 번째. 순서대로 올리기만 하면 문제가 없다. 그런데 중간에 하나를 빼면 뒤가 한 칸씩 당겨진다. 그때부터 순번은 다른 파일을 가리킨다.
이게 원인이라고 단정하지는 않았다. 실제 데이터에서 재현된 적이 없으니까. 대신 순번 대신 각 파일에 고유한 표식을 붙이도록 고쳤다. 유력한 후보 하나를 차단한 것이고, 재발하면 이제 진단 화면으로 확정할 수 있다.
원인을 모르는 채로 닫는 건 찜찜하다. 그래도 "아마 이거일 것"을 "이거였다"로 적어두는 것보다는 낫다. 기록이 틀리면 다음 사람이 — 대개 몇 달 뒤의 나인데 — 그 틀린 기록 위에 판단을 쌓는다.
이 작업에서 남은 건 수정 두 줄이 아니라 화면 하나다.
다음에 같은 제보가 오면 코드를 노려보는 대신 데이터를 연다. 그게 몇 시간을 아낀다. 그런데 이 화면은 버그가 없었으면 안 만들었을 것이다. 기능 목록에 "발송 내역 상세 보기" 같은 건 우선순위가 늘 밀린다.
AI에게 개발을 맡기면서 코드 쓰는 시간은 확실히 줄었다. 대신 "지금 무슨 일이 벌어지는지 볼 수 있나"가 병목이 됐다. AI는 시키면 고쳐준다. 문제는 뭘 고쳐야 하는지를 내가 모른다는 거고, 그건 코드를 더 읽는다고 나오지 않는다.
못 보는 상태에서 고치면, 고친 게 아니라 찍은 거다.