이 글에는 제휴 링크가 없고 OpenAI·앤트로픽과 아무 관계가 없습니다. 제가 직접 만든 중계기의 개발 기록을 바탕으로 썼습니다. 삽화는 GPT 이미지 생성, 도식은 직접 제작했습니다.
‘준비 중’이 끝나지 않는 화면 — 영원히 ‘조리 중’인 주문 번호판 (LLM 중계기 개발기)
카페에서 음료를 주문하고 진동벨을 받았다고 해 볼게요. 계산대 위 번호판에는 칸이 두 개 있어요. ‘조리 중’과 ‘완료’. 내 번호는 ‘조리 중’ 칸에 떠 있고, 뒤에 주문한 사람들의 번호가 하나둘 ‘완료’로 넘어갑니다. 그런데 내 번호만 움직이지 않아요.
사실 주방에서는 내 음료를 만들다 엎었어요. 그런데 번호판에는 ‘취소’ 칸이 아예 없었습니다. 그래서 내 번호는 ‘조리 중’에 계속 떠 있고, 나는 울리지 않을 진동벨을 쥐고 앉아 있어요.
제가 만든 LLM 중계기에서 꼭 이런 일이 있었어요. AI 연결은 이미 끝났는데, 화면의 준비 표시는 ‘워밍업 중’에서 영영 넘어가지 않았습니다. 그것도 모양을 바꿔 두 번이나요.
이 글은 LLM 중계기 개발기의 22편입니다. 지난 21편 「끄는 것도 기술이다」에서는 꺼졌어야 할 프로그램이 뒤에 남은 이야기를 했어요. 오늘은 끝났어야 할 표시가 끝나지 않은 이야기입니다.

결론 요약
- 증상 — AI 연결도 끝났고 채팅도 잘 되는데, 중계기 화면의 준비 표시가 ‘워밍업 중’에서 영영 넘어가지 않았어요. 개발 첫날 밤에는 ‘2/3 준비’에서, 한 달여 뒤에는 ‘0/3 준비’에서요
- 원인 — 첫 번째는 준비에 실패한 모델이 갈 끝 상태가 없어서, 화면이 그 모델을 ‘아직 데우는 중’으로 읽었어요. 두 번째는 아무도 데우지 않는 모델이 스스로 ‘워밍업 중’이라고 보고했어요. 어느 쪽이든 화면이 끝날 조건이 영영 채워지지 않았습니다
- 규칙 — 모든 상태에는 끝이 있어야 해요. 준비는 ‘준비 완료’ 아니면 ‘실패’로 끝나고, 시간 초과도 실패로 셉니다. 지금 진행 중인 일이 없으면 ‘워밍업 중’이 아니라 ‘준비 전’이라고 정직하게 말해요
🧾 1. 주문 번호판으로 보는 ‘상태’
번호판의 칸 하나하나를 상태(state)라고 불러요. 주문 하나는 늘 어느 한 칸에 있고, 일이 진행되면 화살표를 따라 다음 칸으로 옮겨 갑니다.
- 주문 접수 → 조리 중
- 조리 중 → 완료 (잘 만들었을 때)
- 조리 중 → 취소 (엎었거나 재료가 떨어졌을 때)
이렇게 칸과 화살표로 그린 그림을 개발자들은 흔히 상태 기계(state machine)라고 불러요. 이름은 거창하지만 결국 ‘어떤 칸이 있고, 어떤 일이 생기면 어느 칸으로 가는지’를 적어 둔 표예요.
이 중에서 ‘완료’와 ‘취소’는 특별한 칸이에요. 여기 닿으면 더는 기다릴 일이 없거든요. 이런 칸을 끝 상태(terminal state)라고 해요. 손님은 번호가 끝 상태에 닿아야 자리에서 일어날 수 있어요. 음료를 받으러 가든, 다시 주문하든요.
그래서 번호판이 믿을 만하려면 한 가지가 꼭 필요해요. ‘조리 중’에서 나가는 화살표가 모든 경우에 있어야 한다는 것. 잘 만든 경우, 엎은 경우, 주방이 아무 대답도 하지 않는 경우까지요. 화살표가 하나라도 빠지면, 그 길로 들어선 주문은 ‘조리 중’에 영원히 갇힙니다.
중계기 화면에도 이런 번호판이 있어요. 6편에서 중계기는 Claude 같은 AI 프로그램을 미리 데워 둔 엔진처럼 켜 두고 쓴다고 했죠. 화면에는 AI마다 준비 표시줄이 있어서, 모델 몇 개가 준비됐는지와 함께 ‘워밍업 중’이나 ‘준비 완료’를 보여 줘요. 화면은 1.5초마다 중계기에 “지금 어때?”를 물어서 이 표시를 바꿉니다.
🔥 2. 첫 번째 — ‘2/3 준비 · 워밍업 중’에서 멈춘 밤
개발 첫날 밤을 넘긴 새벽 1시 8분, v0.1.17의 저장 기록(커밋)에는 이런 증상이 적혀 있어요. Claude는 실제로 연결이 끝났는데, 화면은 ‘2/3 준비 · 워밍업 중’에 영원히 머물렀다. 21편에서 좀비 프로세스를 고친 지 세 시간쯤 뒤의 일이에요.
그때 중계기는 Claude를 연결하면 이렇게 준비했어요. 지금 방식과는 조금 달라요(4절에서 다시 나옵니다).
- 먼저 가장 가벼운 모델에 짧은 질문을 던져 연결이 되는지 확인하고
- 나머지 두 모델은 뒤에서 미리 데웠어요
그런데 데우던 모델 하나의 세션이 오류로 끝나면, 그다음이 문제였어요.
- 오류로 끝난 세션은 목록에서 그냥 지워졌어요
- 그래서 그 모델의 보고는 ‘데워져 있지 않음’ 한 줄뿐이었어요. ‘데우는 중’도 아니고 ‘실패’도 아닌, 그냥 ‘아님’이요
- 화면의 규칙은 ‘일부는 준비됐는데 전부는 아니면 → 워밍업 중’이었어요
결국 화면은 실패한 모델을 ‘아직 데우는 중’으로 읽었습니다. 엎은 음료를 ‘조리 중’으로 보여 준 번호판과 똑같죠. 기록의 표현으로는, 준비된 모델을 세는 장치에 실패라는 끝이 아예 없었어요. 화면이 끝나는 조건은 ‘전부 준비 완료’뿐이었으니, 화면은 1.5초마다 다시 묻기를 끝없이 되풀이했어요.
같은 시기 Codex는 멀쩡해 보였는데, 기록은 그 이유도 적어 두었어요. Codex는 보통 모델이 하나뿐이라 ‘1/1’이었기 때문이라고요. 모델이 하나면 ‘일부만 준비된’ 상황이 생길 틈이 없죠.
⏱️ 3. 첫 수정 — 모든 준비는 ‘준비 완료’ 아니면 ‘실패’로 끝난다
그날 새벽의 수정은 ‘영원히 대기’를 구조적으로 불가능하게 만드는 쪽이었어요.
① 제한 시간을 두르고, 결과는 둘 중 하나 — 모델마다 데우는 일을 제한 시간으로 감쌌어요. 성공하면 ‘준비 완료’, 오류가 나면 ‘실패’, 제한 시간을 넘겨도 ‘실패’예요. 어느 경우든 세는 장치가 한 칸 나아갑니다. 첫 세션을 켜는 데 꽤 오래 걸릴 수 있어서 제한 시간은 넉넉하게 잡았어요(기본 150초) ② 모든 AI가 함께 쓰는 도구로 — 이 규칙을 Claude 전용으로 두지 않고, 어떤 AI 연결 부품이든 가져다 쓸 수 있는 공용 도구로 만들었어요. 이 도구로 데우면 어느 AI든 ‘워밍업 중’에 영원히 머물 수 없어요. Codex도 같은 모양으로 ‘실패’를 보고하게 맞췄고요 ③ 실패는 이유와 함께 — 실패한 모델은 실패 이유를 함께 보고하고, 화면은 그 모델 이름과 이유를 보여 줘요 ④ 화면이 끝나는 조건을 바꿨다 — 예전에는 ‘전부 준비 완료’여야 끝이었어요. 이제는 ‘아직 워밍업 중인 모델이 하나도 없으면’ 끝이에요. 전부 준비됐든, 일부가 실패했든 상관없이요. 끝에 닿으면 화면은 더 묻지 않아요

그리고 4편부터 이어 온 ‘고장 하나에 검사 하나’ 습관대로 검사를 붙였어요. 기록에는 ‘영구 대기 금지’라는 이름표가 달려 있어요.
- 모델 셋을 흉내 내서 하나는 성공하고, 하나는 일부러 오류를 내고, 하나는 영원히 대답하지 않게 만들어요
- 그래도 제한 시간 안에 셋 모두 끝 상태에 닿는지, ‘워밍업 중’이 0개인지 확인해요
- 대답하지 않은 모델은 ‘시간 초과’라는 이유와 함께 ‘실패’로 끝나야 통과예요
이 검사는 처음에 테스트를 돌릴 때마다 맨 먼저 도는 자리에 들어갔고, 지금은 중복을 정리해 전체 테스트 안에서 돌아요.
실제 화면으로도 확인했어요. 일부러 두 모델이 시간 초과가 나게 하자 표시는 ‘준비 완료 1/3 · 일부 실패’로 끝났고 ‘워밍업 중’은 하나도 없었어요. 평소처럼 연결했을 때는 초록색 ’⚡ 준비 완료 (3/3 warm)‘에 도착했고요.
🧊 4. 두 번째 — 아무도 데우지 않는데 ‘워밍업 중’
한 달여 뒤인 8월 19일, v0.1.71의 기록에 거의 같은 증상이 다시 나와요. AI 연결도 성공했고 채팅도 정상인데, 화면이 ’🔥 워밍업 중… (0/3 준비)‘에서 영원히 멈췄다고요. 이번에는 숫자가 0이었어요.
첫 수정이 무너진 걸까요? 아니었어요. ‘워밍업 중인 모델이 하나도 없으면 끝’이라는 화면 규칙은 그대로 맞았어요. 이번에는 보고가 틀렸어요. 아무도 데우지 않는 모델이 스스로 ‘워밍업 중’이라고 말하고 있었거든요.
그 사이 설계가 바뀌어 있었어요. 무거운 앱 껍데기를 걷어내던 8월 초(25편에서 다룹니다)부터 중계기는 6편에서 소개한 지금 방식으로 일해요.
- 연결할 때 모든 모델을 미리 데우지 않아요. 연결이 되는지만 확인해요(Claude는 가장 가벼운 모델로)
- 요청이 오면 그때 필요한 프로그램만 켜요
- 5분 동안 요청이 없으면 켜 둔 프로그램을 정리해요
그런데 상태를 보고하는 쪽은 이 변화를 따라오지 못했어요. 기록이 꼽은 원인은 두 가지예요.
- ‘모르면 워밍업 중’이라는 기본값 — 데운 적도 없고 지금 데우는 중도 아닌 모델의 기본 보고가 ‘워밍업 중’이었어요. 연결하자마자 나머지 모델을 뒤에서 데우던 시절에는 잠깐 맞는 말이었지만, 그 일을 아무도 하지 않게 되자 영원한 거짓말이 됐어요. 기록에는 모델별 워밍업을 실제로 부르는 코드가 하나도 없다고 적혀 있어요
- 5분 정리 뒤의 빈칸 — 5분 동안 쓰지 않아 정리가 돌면 ‘연결 확인을 마쳤다’는 기록까지 함께 지워졌어요. 채팅은 그 기록 없이도 잘 돌아갔고, 그래서 아무도 그 기록을 다시 채우지 않았어요. 상태 보고는 그 기록을 기준으로 삼았기 때문에 모든 모델이 ‘워밍업 중’(0/3)으로 남았어요. Codex도 쉬는 동안 앱 서버가 내려가 있으면 ‘워밍업 중’이라고 보고했고요
검사 하나도 틀린 정답을 지키고 있었어요. Claude 연결 부품의 검사가 ‘연결 직후 다른 모델은 워밍업 중’을 정답으로 적어 두고 있었거든요. 뒤에서 데우던 시절의 정답이 그대로 남아 있었던 거예요.
번호판으로 치면 이래요. 주방 불은 꺼져 있고 요리사는 쉬러 갔는데, 번호판에는 ‘조리 중’이 켜져 있는 거예요. 아무도 만들고 있지 않은 주문인데요. 정직한 표시는 ‘주문하시면 그때 만들어요’예요.
이번 수정의 원칙은 기록에 한 줄로 적혀 있어요. warming은 실제 진행 중일 때만.
- 지금 실제로 데우는 일이 돌고 있을 때만 ‘워밍업 중’이에요
- 그 밖의 데워지지 않은 모델은 새 끝 상태 준비 전(cold)으로 보고해요. 지금 진행 중인 일은 없고, 첫 요청이 오면 그때 자동으로 데운다는 뜻이에요. 6편의 말로 하면 엔진이 식어 있다는 거예요
- Claude는 실제로 살아 있는 세션이 있는 모델을 ‘준비 완료’로 올려요. 쓰다 보니 데워진 모델도 정직하게 준비 완료로 보이게요
- Codex는 쉬는 동안 앱 서버가 내려가 있으면 ‘준비 전’이에요. 앱 서버를 켜는 중일 때만 ‘워밍업 중’이고요
화면 표시도 언제나 끝나게 바꿨어요.
- 전부 데워져 있으면 ’⚡ 준비 완료’
- 일부만 데워져 있으면 ’⚡ 사용 가능 — n/m warm · k개는 첫 요청 시 자동 워밍업’
- 경고 색은 진짜 실패가 있을 때만
릴리스 노트는 이 수정이 표시의 약속만 고친 것이라고 적었어요. 요청을 어디로 보내는지, 언제 데우는지는 하나도 바뀌지 않았어요. 바뀐 건 화면이 하는 말뿐이에요. 앞의 틀린 정답은 ‘준비 전’으로 고쳤고, 한 번도 데운 적 없는 모델과 5분 정리 뒤의 모델이 ‘준비 전’인지, 살아 있는 세션이 있는 모델이 ‘준비 완료’인지, Codex 앱 서버가 내려가 있을 때 ‘준비 전’인지 확인하는 검사도 더했어요.

🔁 5. 같은 병의 세 번째 얼굴 — ‘적용 중’에서 멈춘 업데이트
끝나지 않는 표시는 워밍업에만 생기지 않았어요. 9월 29일 v0.1.108의 기록은 자동 업데이트 이야기예요. 새 버전을 찾고 내려받기까지 했는데, 설치가 진행되지 않고 ‘적용 중’에서 멈췄어요.
중계기의 업데이트는 이렇게 적용돼요.
- 화면에서 적용을 누르면 중계기 서버가 “설치를 시작해”라는 신호를 자기 출력에 한 줄로 적어요
- 그 서버를 띄운 트레이(화면 구석의 작은 아이콘 프로그램)가 그 줄을 읽고 설치 프로그램을 실행해요
그날은 같은 문 번호에 이미 다른 중계기 서버가 떠 있었어요. 개발하면서 직접 띄워 둔 서버였어요. 릴리스 노트는 지난 실행에서 남은 서버도 같은 일을 일으킬 수 있다고 적었어요(21편의 좀비가 떠오르죠). 설치된 트레이는 자기 서버를 새로 띄우지 않고 이미 떠 있던 그 서버에 조용히 붙었어요. 설정 화면도 업데이트 버튼도 그 서버의 것이 됐고, 신호는 나갔지만 그 줄을 읽을 트레이가 없었어요. 그래서 ‘적용 중’은 끝나지 않았습니다. 화면에 보이던 버전도 설치본이 아니라 그 서버의 버전이었고요.
이번 해결은 조금 달랐어요. 끝을 만들어 주는 대신, 끝낼 주체가 없는 일은 시작하지 않게 했어요.
- 설치된 트레이가 띄운 서버만 업데이트를 적용해요
- 그렇지 않은 서버는 ‘적용 중’으로 들어가지 않고 바로 거절해요. 상태는 ‘대기’에 그대로 머물러요
- 화면은 내려받기·적용 버튼 대신 이유를 보여 줘요. ‘이 서버를 종료하고 설치된 중계기를 다시 실행해 업데이트하세요’라는 뜻의 안내예요
- 트레이도 다른 서버에 붙으면 ‘다른 중계기에 연결됨’이라는 뜻의 알림을 띄워요
업데이트에서 겪은 다른 일들은 다음 편들에서 이어 갈게요.
📋 6. 남긴 규칙 — 모든 상태에는 끝이 있다
세 번의 일에서 남은 규칙을 번호판의 말로 정리하면 이래요.
- 진행 중에서 나가는 화살표를 모두 그린다 — 성공, 실패, 그리고 아무 대답이 없는 경우까지요. 대답이 없으면 정해 둔 시간이 지났을 때 ‘실패’로 옮겨요
- 실패도 끝이다 — 실패는 숨길 일이 아니라 도착점이에요. 이유를 함께 보여 주면 기다리던 사람이 다음 행동을 고를 수 있어요
- 모르면 ‘진행 중’이라고 하지 않는다 — 아무 일도 진행되지 않으면 ‘준비 전’이에요. 기본값을 ‘진행 중’으로 두면, 설계가 바뀌는 날 그 기본값이 영원한 거짓말이 돼요
- 화면이 짐작하게 두지 않는다 — 첫 번째 사고에서 화면은 ‘일부만 준비됨’을 보고 ‘워밍업 중’이라고 짐작했어요. 지금은 각 모델이 ‘준비 완료·워밍업 중·실패·준비 전’ 중 하나를 직접 이름으로 보고해요
- 끝낼 주체가 없으면 시작하지 않는다 — 끝까지 데려갈 주체가 없는 일은 시작 전에 거절하고 이유를 말해요
- 검사로 지킨다 — ‘영구 대기 금지’ 검사는 실패와 무응답을 일부러 섞어도 ‘워밍업 중’이 0개로 끝나는지 봐요. 두 번째 사고 뒤에는 ‘준비 전’을 확인하는 검사도 더해졌어요

정리
① 화면의 ‘워밍업 중’은 번호판의 ‘조리 중’과 같아요. 여기서 나가는 화살표가 하나라도 빠지면 표시는 영원히 끝나지 않아요 ② 첫 번째는 실패한 준비가 갈 칸이 없었고(2/3 준비), 두 번째는 아무도 데우지 않는 모델이 ‘워밍업 중’이라고 보고했어요(0/3 준비). 실패와 시간 초과를 끝 상태로 만들고, 진행 중인 일이 없으면 ‘준비 전’이라고 말하게 고쳤어요 ③ 같은 병이 업데이트 ‘적용 중’에서도 나타나서, 끝낼 주체가 없는 일은 시작하기 전에 이유와 함께 거절하게 했어요
다음 23편은 「자동 업데이트가 계속 되돌아간 이유」예요. 이번 편과 반대로, ‘실패’라는 끝을 너무 일찍 정해 버려서 새 버전이 자꾸 옛 버전으로 되돌아가던 이야기를 할게요.
2026년 10월 1일 기준입니다. 제가 만든 중계기의 개발 기록(저장 기록의 시각과 메모, 릴리스 노트)을 바탕으로 썼고, 특정 기업·서비스의 공식 입장이 아닙니다.