열 번의 수선, 한 글자
백지로 나오는 책 한 권과, 침묵하는 기계에 대하여
깨달음: "고장이 클수록 원인은 작다. 그리고 기계는 거짓말을 하지 않지만, 침묵은 한다."
스택튜브에는 한 달치 노트를 한 권의 전자책으로 묶어주는 기능이 있다. 월말이 되면 그달에 분석된 영상 노트들이 챕터가 되고, 표지가 입혀지고, 이메일과 킨들로 배달된다. 월간 무크지라고 부른다. 이 기능을 만들던 주말의 이야기다.
빌드는 성공했다. 파일도 생성됐다. 용량도 그럴듯했다. 그런데 책을 열면, 백지였다.
목차는 있는데 본문이 없는 책. 에러 메시지는 없었다. 시스템의 어디를 봐도 "성공"이라고 적혀 있었다. 성공했다는 백지 책. 이것이 이 사건의 가장 고약한 점이었다.
에러가 나면 에러 메시지를 AI에게 보내면 된다. 그건 이제 무섭지 않다. 그런데 에러가 없으면 보낼 것이 없다. "책이 비어 있어"라는 말은 단서가 아니라 증상이다. 병원에 가서 "아파요"라고만 말하는 환자가 된 기분이었다.
그날부터 수선이 시작됐다. 폰트 패키지가 빠졌나 — 고쳤다. 백지. 스타일시트가 연결이 안 됐나 — 고쳤다. 백지. 배포 설정에서 파일이 누락됐나 — 고쳤다. 백지. 변환기 옵션인가 — 고쳤다. 백지. 한 번 고칠 때마다 "이번엔 됐다"는 확신이 있었고, 매번 책은 비어 있었다.
고치는 것마다 실제로 문제이긴 했다. 폰트도, 스타일시트도, 배포 설정도 전부 손봐야 할 곳이었다. 그래서 더 헷갈렸다. 진짜 범인이 따로 있는데, 잡범들이 계속 잡히는 상황.
일곱 번째 수선에서 AI가 범인을 찾았다. 원인은 코드 한 줄. 정확히는, 문자열을 바이트로 바꿔주는 변환 하나가 빠져 있었다.
설명을 듣고도 한참 이해하지 못했다. 요약하면 이렇다. 책의 각 페이지를 만드는 라이브러리가 있고, 그 라이브러리에 글을 넘길 때는 특정한 형식이어야 했다. 형식이 맞지 않으면 라이브러리는 거부한다. 여기까지는 정상이다. 문제는 그다음이었다. 중간에 있던 또 다른 라이브러리가 그 거부의 비명을 받아서, 아무에게도 전하지 않고 삼켰다. 그리고 빈 값을 돌려줬다. 그래서 모든 페이지가, 조용히, 비어 있었다.
수정은 .encode("utf-8") — 한 글자도 안 되는 분량은 아니지만, 한 줄이다. 이 한 줄을 넣자 712,841바이트짜리 책이 나왔다. 본문이 있는 책이었다.
그날 밤에 생각했다. 열 번의 수선 중 아홉 번이 헛수고였나? 아니었던 것 같다. 잡범들을 다 치우고 나서야 진범이 보였다. 용의자가 열 명일 때는 추리가 안 된다. 아홉 명의 알리바이를 확인하는 일은 지루하지만, 그게 수사다.
그리고 또 하나. 나는 이제 "성공"이라는 말을 덜 믿게 됐다. 시스템이 성공이라고 말하는 것과 결과물이 멀쩡한 것은 다른 문제다. 사람도 비슷하다. 괜찮다고 말하는 것과 괜찮은 것은 다르다. 기계는 거짓말을 하지 않는다고 했지만, 정정한다. 기계는 적극적으로 거짓말을 하지는 않는다. 다만 불리한 것을 말하지 않을 뿐이다.
🔧 이 에피소드의 기술 용어 해설
EPUB 전자책 표준 파일 형식. 내부적으로는 여러 개의 웹 페이지(XHTML)를 압축해 묶은 것이다. 킨들, 애플 북스 등에서 읽을 수 있다.
문자열과 바이트 (String / Bytes)
사람이 읽는 글자(문자열)와 기계가 저장하는 0과 1(바이트)은 다른 형식이다. 둘 사이를 오갈 때는 변환(인코딩)이 필요하다. .encode("utf-8")이 그 변환이다.
사일런트 페일러 (Silent Failure) 실패했는데 에러를 알리지 않고 조용히 지나가는 것. 가장 찾기 어려운 종류의 버그다. 에러는 단서지만, 침묵은 단서가 없다.
핫픽스 (Hotfix) 운영 중인 서비스의 긴급 수정. 이 사건에는 핫픽스가 열 번 있었고, 일곱 번째가 진범이었다.