2026년 10월 13일 07:43경, 폰에서 AI 직원에게 메시지를 보내는 화면에 오류가 떴어요. 전송 입력 아래뿐 아니라 상단 설정을 불러오는 곳에도 같은 문구가 보였어요. 그런데 다음 캡처에는 보낸 메시지와 직원 답변이 있었고, 설정 오류만 남아 있었어요. 화면에 오류가 떠도 메시지는 이미 도착할 수 있다는 상황이 드러났어요.
저는 AI 직원들의 업무와 대화를 한곳에서 보는 자동화 콘솔을 쓰고 있어요. 폰에서도 직원에게 글과 첨부를 보내고 진행 상태를 확인해요. 이번에는 그 대화 화면을 맡은 Codex 에이전트가 응답 처리와 재전송 방식을 함께 고쳤어요.
JSON을 기다렸는데 웹페이지가 왔어요
캡처에 나온 문구는 이랬어요.
Unexpected token '<', "<!DOCTYPE "... is not valid JSON
JSON은 프로그램끼리 주고받는 데이터를 일정한 형식으로 적는 방식이에요. 화면은 메시지 전송 결과나 설정값을 그 형식으로 받으려고 했어요. 그런데 응답에는 웹페이지를 시작할 때 쓰는 <!DOCTYPE이 들어 있었어요. 웹페이지 내용을 JSON으로 읽으려다 첫 글자 <에서 막힌 거예요.
기존 코드는 응답의 종류를 먼저 확인하지 않고 response.json()을 호출했어요. 설정 조회는 일부 연결 오류만 다시 시도하도록 되어 있어서, HTML을 읽다 난 오류는 화면에 계속 남을 수 있었어요.
여기까지는 코드와 캡처로 확인했어요. 그 웹페이지가 어디서 왔는지는 별개였어요. 당시 폰의 HTTP 상태 코드와 리디렉트 기록이 없어, 로그인 화면이었는지 중간 연결 장치의 오류 페이지였는지는 특정하지 못했어요. HTTP 상태 코드는 요청 결과를 숫자로 알려 주고, 리디렉트는 다른 주소로 이동시키는 응답이에요.
다시 보내기 전에, 같은 전송인지 알아야 했어요
응답 처리를 고치면서 더 중요해진 건 재시도였어요. 설정을 읽는 요청은 잠시 뒤 다시 읽으면 되지만, 메시지 전송에는 저장이 따라와요. 서버가 글을 저장한 직후 확인 응답만 끊기면, 폰은 성공 여부를 모른 채 같은 글을 또 보낼 수 있어요.
Codex는 각 메시지에 UUID라는 고유한 전송 ID를 붙였어요. 한 번 보내려던 글과 첨부에는 같은 ID를 유지하고, 서버는 그 ID와 내용의 지문을 함께 대조해요. 내용의 지문은 글과 첨부가 같은지 비교하는 값이에요. 이미 저장한 전송이면 재시도를 받아도 메시지와 첨부를 추가로 저장하지 않아요. 같은 ID에 다른 내용을 넣으면 거부하고, 새 글은 새 ID로 보내게 했어요.
이렇게 같은 요청을 반복해도 저장 결과가 더 늘어나지 않게 만드는 성질을 멱등성이라고 해요. 이번 콘솔에서는 전송 ID와 저장 기록을 연결해 그 성질을 만들었어요. 오래된 대화의 메모리를 정리한 뒤에도 저장 기록으로 재전송을 알아보도록 했고요.
자동으로 다시 시도하는 범위도 좁혔어요. 조회와 전송 ID가 있는 메시지만 최대 3번의 시도 안에서 처리해요. 로그인 재확인이 필요한 응답이나 입력 거부는 반복하지 않고, 다른 설정 변경 요청도 자동 재시도에 넣지 않았어요. 큰 첨부 업로드가 시간 초과로 끝난 경우에도 자동으로 반복 전송하지 않게 했어요.
실패한 글과 첨부는 원래 탭의 입력 화면에 남겨요. 인증을 다시 해야 할 때는 ‘다시 연결’을 별도 탭으로 열어 로그인한 뒤 돌아올 수 있어요. 다만 원래 탭을 닫거나 새로고침하면 미전송 초안은 사라져요. 초안을 유지하려면 그 탭을 열어 둬야 해요.
요청은 네 번, 저장된 메시지는 두 건이었어요
Codex는 모바일 크기의 시험 브라우저에서 서버가 저장한 뒤 응답만 잃는 상황을 만들어 확인했어요. 이런 합성 검증은 실제 장애와 비슷한 조건을 일부러 넣어 동작을 검사하는 방식이에요. 실제 직원에게 시험 메시지를 보내지는 않았어요.
| 시험에서 넣은 상황 | 확인한 결과 |
|---|---|
| 서버 저장 뒤 응답 유실, 같은 ID로 재시도 | 전송 요청 4회에 메시지 2건만 저장됐어요. |
| 같은 ID로 글이나 첨부 내용 변경 | 변경된 내용은 거부됐어요. |
| 인증 실패 | 글과 이미지 초안이 원래 화면에 남았어요. |
| 조회에 HTML 일시 오류를 넣은 뒤 정상 응답 | 설정과 대화 조회가 회복됐어요. |
별도 코드 검사 55개에서는 동시에 같은 ID로 재전송하는 경우와 기억 정리 뒤 재전송도 확인했어요. 글과 첨부가 한 번만 저장됐어요. 검토를 맡은 Claude는 큰 업로드에 조회용 60초 제한이 적용되는 문제를 짚었고, Codex가 그 제한을 분리했어요. 상단 설정에도 실제로 다시 연결할 링크가 있는지 마지막 화면에서 확인했어요.
07:53:32에는 다음 메시지로 수정이 커밋됐어요.
모바일 콘솔 연결 오류를 복구하고 메시지 재전송 중복을 막는다
응답의 종류를 구분하고, 재시도할 때 같은 전송임을 알아보도록 바꿨다는 내용이에요. 실제 콘솔이 제공하는 HTML 버전과 현재 자바스크립트 빌드가 일치하는 것도 확인했어요. 마지막 회사 상태 조회 네 번은 모두 HTTP 200으로 응답했고, 걸린 시간은 541~979밀리초였어요.
폰에서 다시 나타나면 무엇을 볼까요
현재 확인한 결말은 수정된 콘솔의 정상 조회와 합성 모바일 조건에서의 중복 방지예요. 실제 폰에서 같은 오류가 재발하는지, 느린 대용량 첨부와 로그인 뒤 복귀가 잘 되는지는 후속 사용에서 확인해야 해요. 처음 HTML 응답을 만든 원인도 아직 미확정이에요.
비슷한 전송 화면을 운영한다면, 시험 서버에서 저장 직후 확인 응답을 끊어 보세요. 같은 전송 ID로 다시 보냈을 때 저장된 글과 첨부가 늘어나는지 세면, 화면의 성공 표시만으로는 놓치던 부분을 확인할 수 있어요.
이번 수정으로 브라우저는 실패한 요청의 방식·상태 코드·오류 종류를 기록해요. 제 콘솔에서 오류가 다시 보이면 이 기록을 같은 시각의 서버 기록과 맞춰, 처음 HTML 응답이 어디서 왔는지 찾아볼 거예요. 메시지 본문이나 인증 주소는 진단 기록에 넣지 않아요.