쓰던 AI 모델이 없어졌습니다 — 모델명을 코드에 박지 않는 법
어제까지 잘 돌던 자동화가 갑자기 실패하기 시작했습니다. 코드는 한 줄도 안 건드렸습니다.
쓰던 AI 모델이 없어져 있었습니다. 모델명을 코드에 그대로 적어 뒀는데, 그 모델이 목록에서 빠지면서 호출이 통째로 실패했습니다.
이 글은 그걸 다시 안 겪는 방법입니다. 핵심은 하나입니다 — 모델명을 코드에 박지 않습니다.
1. 사용 가능한 모델 목록을 조회합니다 2. 우선순위대로 살아 있는 것을 고릅니다 3. 무엇을 골랐는지 기록에 남깁니다 4. 목록이 바뀌면 알림이 오게 합니다
AI모델 단종 증상 — 코드는 그대로인데 결과가 다릅니다
증상 — 어제와 똑같은 코드가 오늘은 실패합니다. 배포한 적도, 설정을 바꾼 적도 없습니다.
원인 — 모델이 제공 목록에서 빠졌습니다. AI 서비스는 모델을 계속 갈아치웁니다. 새 모델이 나오면 오래된 것은 정리합니다.

이게 찾기 어려운 이유가 있습니다. 내 쪽에서 바뀐 게 하나도 없기 때문입니다. 저는 서버를 의심하고, 네트워크를 의심하고, 인증을 의심했습니다. 코드를 안 건드렸으니 코드 문제일 리가 없다고 생각한 겁니다.
같은 코드를 어제 실행한 기록과 오늘 실행한 기록을 나란히 놓고 봤습니다. 요청도 같고 응답 시간도 비슷한데 결과만 달랐습니다. 그제서야 응답 본문을 열어 봤고, 모델을 못 찾겠다는 말이 거기 적혀 있었습니다.
오류 메시지를 끝까지 읽었으면 훨씬 빨랐습니다. 저는 응답 코드만 보고 원인을 짐작했습니다. 그게 습관이 되면 이런 종류를 계속 놓칩니다.
오류 메시지 구분
| 메시지에 나오는 말 | 원인 |
|---|---|
model not found·does not exist |
모델이 없어졌거나 이름이 틀림 |
model is deprecated |
곧 없어짐. 아직은 돌아감 |
you do not have access |
모델은 있는데 내 계정에 권한이 없음 |
401·403 |
모델과 무관. 인증·권한 문제 |
두 번째가 중요합니다. deprecated 경고가 뜨면 그때가 옮길 시점입니다. 그걸 무시하면 어느 날 첫 번째로 바뀝니다.
저는 경고를 본 기억이 없는데, 로그를 자세히 안 봐서 놓쳤을 수도 있습니다. 실제로 경고가 있었는지는 확인하지 못했습니다.
해결 — 모델명을 박지 말고 목록에서 고릅니다
모델명을 코드에 적는 대신, 쓸 수 있는 것 중에서 고르게 만듭니다.
PREFERRED = ["원하는-모델-A", "원하는-모델-B", "무난한-대체-모델"]
def pick_model(available: list[str]) -> str:
"""우선순위대로 살아 있는 첫 번째를 고른다."""
for name in PREFERRED:
if name in available:
return name
if not available:
raise RuntimeError("쓸 수 있는 모델이 없습니다")
return available[0] # 아무것도 안 맞으면 남은 것 중 하나

고른 결과를 기록에 남기는 게 중요합니다. 어느 모델로 돌았는지 모르면 결과가 달라졌을 때 원인을 못 찾습니다.
model = pick_model(list_models())
log(f"이번 실행 모델: {model}")
우선순위 목록도 설정 파일로 빼두면 더 좋습니다. 모델을 바꿀 때 코드를 고칠 이유가 없어집니다.
목록 조회에도 함정이 있습니다
목록을 매번 부르면 호출 수가 늘고 느려집니다. 그렇다고 오래 캐시하면 단종을 늦게 압니다.
| 방식 | 문제 |
|---|---|
| 매번 조회 | 호출 수·지연이 늘어납니다 |
| 오래 캐시 | 없어진 모델을 계속 씁니다 |
| 하루 한 번 조회 + 실패 시 즉시 재조회 | 쓸 만합니다 |
저는 마지막 방식을 씁니다. 평소엔 캐시한 목록을 쓰고, 호출이 모델 문제로 실패하면 그때 목록을 다시 받아 고릅니다. 한 번은 실패하지만 두 번째부터는 알아서 넘어갑니다.
여기서 주의할 게 하나 있습니다. 모델 문제일 때만 다시 받아야 합니다. 어떤 실패든 목록을 새로 받게 만들면, 인증이 끊겼을 때 목록 조회까지 계속 시도하면서 호출만 늘어납니다. 오류 종류를 구분해서 처리해야 합니다.
try:
call(model)
except ModelNotFound:
model = pick_model(list_models(force=True)) # 목록을 새로 받아 다시 고른다
call(model)
모델 단종을 미리 아는 장치
실패한 뒤에 아는 것보다 사라지기 전에 아는 편이 낫습니다.

하루 한 번 목록을 받아서 내가 쓰는 모델이 아직 있는지 대조합니다. 빠졌으면 그날 알립니다.
available = list_models()
missing = [m for m in PREFERRED if m not in available]
if missing:
notify(f"우선순위 모델이 목록에서 빠졌습니다: {missing}")
이걸 붙여두면 자동화가 멈추기 전에 손볼 시간이 생깁니다. 저는 이 점검을 다른 정기 점검에 끼워 넣어서 따로 도는 작업을 늘리지 않았습니다.
모델이 바뀌면 결과도 바뀝니다
여기가 진짜 문제입니다. 호출이 성공해도 결과물의 성격이 달라집니다.
| 바뀔 수 있는 것 | 영향 |
|---|---|
| 문장 길이·어투 | 글이 갑자기 길어지거나 딱딱해집니다 |
| 지시 따르는 정도 | 형식을 어기기 시작합니다 |
| 응답 형식 | JSON을 요청했는데 설명이 섞여 나옵니다 |
| 처리 속도·비용 | 작업 시간과 요금이 달라집니다 |
그래서 모델이 바뀐 날은 결과물을 사람이 한 번 봐야 합니다. 자동으로 넘어간 것까지는 좋은데, 품질이 그대로라는 보장은 없습니다.
저는 결과물에 점수를 매기는 검사를 따로 두고 있어서, 모델이 바뀌어도 기준 미달이면 발행이 막힙니다. 검사가 없다면 최소한 모델이 바뀐 날은 알림이 오게 해두는 편이 낫습니다.
확인 순서
코드를 안 건드렸는데 갑자기 실패한다면 이 순서로 봅니다.
1. 오류 메시지에 모델 이름이 나오나
2. 그 모델이 지금 목록에 있나
3. deprecated 경고가 이전 로그에 있었나
4. 코드에 모델명이 박혀 있나
5. 바꾼 뒤 결과물 품질이 그대로인가
판정 — 계속 쓴다
목록에서 고르는 방식은 붙일 값어치가 있습니다. 코드 열 줄 남짓이고, 어느 날 갑자기 멈추는 일을 없앱니다.
설정 파일로 빼두면 더 낫습니다. 모델을 바꾸는 데 코드를 고치고 다시 올릴 이유가 없습니다.
다만 결과 품질까지 자동으로 지켜지진 않습니다. 모델이 바뀌면 결과물도 바뀝니다. 그건 검사를 따로 두거나 사람이 봐야 합니다. 이 부분을 자동화만으로 푸는 방법은 저도 못 찾았습니다.
직접 하실 분께
하나. 모델명을 코드에 박지 마세요. 지금 되는 것이 다음 달에도 된다는 보장이 없습니다.
둘. 어떤 모델로 돌았는지 기록에 남깁니다. 결과가 달라졌을 때 이게 없으면 원인을 못 찾습니다.
셋. deprecated 경고를 무시하지 않습니다. 그때가 옮길 시점입니다.
넷. 모델이 바뀐 날은 결과물을 한 번 봅니다. 호출이 성공한 것과 품질이 유지된 것은 다릅니다.
다음에는 모델이 바뀌었을 때 결과물 품질이 유지되는지 자동으로 대조하는 방법을 만들어보려고 합니다. 되면 이어서 적겠습니다.