모델이 중단되기 전에 CI를 중단시키세요
요약
AI 모델의 지원 종료(deprecation)는 코드 변경이 아닌 달력상의 이벤트이므로, CI 시스템에서 이를 감지할 수 있어야 합니다. 모델 ID가 하드코딩되어 있으면 서비스 중단 위험이 크므로, AI Model Watch와 같은 도구를 활용해 모델의 라이프사이클을 지속적으로 검증해야 합니다.
핵심 포인트
- 모델 지원 종료는 코드 변경이 아닌 날짜 기반 이벤트임
- 이메일 공지는 계정 소유자에게 전달되어 개발자가 놓치기 쉬움
- 모델 ID 하드코딩은 서비스 중단의 주요 원인이 됨
- CI 단계에서 모델의 상태와 만료일을 검증하는 프로세스 필요
오늘, 2026년 8월 5일, claude-opus-4-1이 은퇴합니다. Anthropic은 지난 6월 5일, 모델 지원 종료 (model-deprecations) 페이지에 해당 날짜를 텍스트로 명시하며 이를 발표했습니다. 두 달간의 사전 공지 기간이 있었습니다.
만약 해당 ID가 저장소(repo) 어딘가에 하드코딩되어 있다면, 빌드 시스템(build system)의 그 어떤 것도 이를 알아채지 못했습니다. 테스트는 여름 내내 통과되었습니다. 린터(linter)는 아무런 의견도 내놓지 않았습니다. 간접 의존성(transitive dev dependency)이 4.2.1에서 4.2.2로 변경되어 PR을 생성할 의존성 봇(dependency bot)조차, 당신의 제품이 기반하고 있는 모델이 타인의 달력에 만료일을 가지고 있다는 사실을 전혀 알지 못합니다.
이것이 문제의 실제 모습입니다: 모델이 사라지는 것은 코드 변경이 아니라 달력상의 이벤트입니다. CI에서 달력을 감시하는 것은 아무것도 없습니다.
제공업체의 이메일이 당신을 보호해주지 못하는 이유
공지는 분명히 존재합니다. Anthropic, OpenAI, Google 모두 지원 종료(deprecation) 통지를 이메일로 보냅니다. 계약상 그렇게 해야 할 의무가 있기 때문입니다. 하지만 그들은 계정 소유자(account owner), 즉 조직(org)에 등록된 카드의 주인에게 이메일을 보냅니다. 저장소(repo)가 아닙니다. 풀 리퀘스트(pull request)가 아닙니다. 8개월 전에 해당 ID를 고정해두고 퇴사한 그 사람도 아닙니다.
따라서 통지는 결제 관련 수신함(billing inbox)에 도착하고, 해당 ID는 호출이 404 에러를 내뱉기 시작할 때까지 src/llm.ts에 그대로 남아있게 됩니다. 이 두 사건 사이에는 보통 빌드 시스템이 당신에게 알려줄 수 있었음에도 알려주지 않은 채 흘러간 수개월의 시간이 존재합니다.
오늘자 카탈로그의 수치들
저는 모델 가격, 컨텍스트 제한(context limits), 라이프사이클 날짜를 매일 검증하여 관리하는 AI Model Watch를 운영하고 있으며, 모든 행에는 해당 정보를 가져온 제공업체의 URL이 포함되어 있습니다. 이 글을 쓰는 동안 라이브 피드에서 가져온 데이터(updated: 2026-08-05, 204개 모델)는 다음과 같습니다:
- 204개 중 74개가 이미
deprecated(지원 종료 예정) 또는retired(은퇴) 상태입니다. - 12개가 향후 90일 이내에 은퇴 날짜를 가지고 있습니다 (오늘 은퇴하는 모델을 포함하면 13개).
이 숫자들을 단순히 더하지 마세요. 12개 중 9개는 이미 74개에 포함되어 있습니다. 지원 종료(deprecated) 상태인 것과 은퇴 날짜가 임박한 것은 별개의 집단이 아니라 일반적인 사례입니다. 따라서 별도의 총합은 86이 아니라 77입니다. 한 단락 내에서 두 번 숫자를 세는 것은 집합(set)의 문제입니다. 그리고 이들을 연결하는 단어는 여타 주장들과 마찬가지입니다.
흥미로운 세 가지는 74번에 포함되지 않은 것들입니다. qwen3-max는 오늘 현재 ga (General Availability, 일반 가용성) 상태이며, 오늘 기준 가격이 책정되어 있고, 현재 문서상으로는 완전히 건강해 보입니다. 하지만 Alibaba의 자체 폐기(deprecation) 테이블에 따르면 이 모델은 9월 8일에 은퇴하며, qwen3.7-max를 가리키고 있습니다. 상태(status) 필드는 결코 당신에게 경고하지 않았을 것입니다. 오직 날짜만이 경고합니다.
확인하는 데 30초
파일 하나, 의존성 없음, 키 없음, 가입 필요 없음, Node 18+:
curl -sLO https://aimodelwatch.dev/ci/check-models.mjs
node check-models.mjs --scan .
--scan은 소스 코드를 탐색하여 알고 있는 모델 ID처럼 보이는 모든 것을 골라내므로, 첫 실행 시 설정할 것이 아무것도 없습니다. 다음은 세 줄짜리 피스처(fixture)를 대상으로 실행한 실제 결과입니다. Windows 경로 구분자만 정규화되었을 뿐, 출력된 그대로의 결과입니다:
$ node check-models.mjs --scan .
check-models 1.1.0 — feed updated 2026-08-05, 204 models
scanned . — found 3 model ids in your source
...
세 개의 ID, 세 개의 제공업체(provider), 세 가지 서로 다른 실패 모드: 하나는 오늘 만료되고, 하나는 5주 뒤의 날짜와 함께 여전히 GA로 표시되어 있으며, 하나는 분기가 1개 남은 상태로 폐기(deprecated)되었습니다. 이들 모두에서 Exit 1(종료 코드 1)이 발생합니다.
(제 소스 코드의 ID는 gemini-2.5-flash이지만 결과는 gemini-2-5-flash로 나옵니다. 제공업체들이 페이지에 따라 동일한 모델을 두 가지 방식으로 작성하기 때문에, ID는 정규화된 형식으로 매칭됩니다. JSON 보고서를 grep으로 검색할 때 알아두면 유용한 정보입니다.)
GitHub Actions에서
액션(action)이 존재하므로, 이것이 전체 설정의 전부입니다. 설치 단계도, setup-node도, 입력값(inputs)도 필요 없습니다:
# .github/workflows/model-lifecycle.yml
name: model lifecycle
on:
...
입력값 없이도 저장소(repo)를 탐색하여 소스 내의 모든 모델 ID를 찾아내고 모두 확인합니다. 결과는 문제가 되는 라인에 인라인 ::error 주석(annotation)과 작업 요약(job-summary) 테이블로 반환됩니다. 이 액션은 스크립트를 공급(vendor)하므로, @v1은 코드를 고정(pin)하는 동안 카탈로그는 실시간으로 유지됩니다.
크론(cron) 라인이 중요한 나머지 절반입니다. pull_request 트리거는 누군가가 PR을 여는 날에만 도움이 됩니다. 하지만 지원 종료(Deprecations)는 아무도 PR을 열지 않는 날에 발표됩니다. 이것이 캘린더 이벤트(calendar event)의 본질입니다. 주간 실행(weekly run)은 "8월에 알게 된 것"을 "6월에 알게 된 것"으로 바꿔줍니다.
나머지 절반: 가격도 변합니다
모델이 더 저렴해지거나 비싸진다고 해서 이메일을 보내주지는 않습니다. 출력 가격(output-price)이 40% 인상된다고 해서 지원 종료 공지가 뜨지는 않습니다. 그저 월말에 청구서의 형태가 바뀔 뿐입니다.
그러므로 제품 비용을 산정할 때 사용했던 수치들을 고정(pin)하고, 해당 파일을 커밋(commit)하세요:
node check-models.mjs --scan . --pin .amw-pin.json --update-pin # 한 번 실행 후 커밋
node check-models.mjs --scan . --pin .amw-pin.json # 빌드마다 실행
고정(pin) 파일은 크기가 작고 차이점(diff)을 확인하기 쉽습니다. 모델별로 상태(status), 세 가지 가격, retires_on, 명시된 대체 모델(replacement) 정보가 포함됩니다. 값이 고정된 값에서 벗어나면, 두 수치를 모두 포함한 빌드 실패(build failure) 메시지가 나타납니다:
✖ qwen3-max: Qwen3-Max input price ($/1M) changed since your pin: 0.8 → 1.2.
(위 예시는 제가 고정 파일을 예전 값으로 수정하여 만든 것입니다. 논점을 증명하기 위해 가격 변동을 조작하지는 않겠습니다. 핵심은 메커니즘입니다. 이 메커니즘은 커밋된 스냅샷(snapshot)을 오늘의 카탈로그(catalog)와 비교합니다.)
미리 밝혀두는 주의사항 (Sharp edge)
GitHub는 컴포지트 액션(composite action)이 작업을 실패했을 때 해당 액션의 출력값(outputs)을 평가하지 않습니다. 따라서 당신이 가장 신경 써야 하는 부분, 즉 실패한 단계의 steps.*.outputs.*는 빈 값으로 반환됩니다.
컴포지트 액션 내부에서는 이를 해결할 수 없었기에, 대신 JSON 보고서가 조건 없이 작성되도록 했습니다. report-path를 특정 위치로 지정한 뒤 if: always()를 사용하여 읽거나, warn-only: true로 설정한 후 출력값을 직접 분기 처리하세요. 출력값을 기반으로 Slack 알림을 구축했다가 왜 빈 메시지만 올라오는지 의아해하기 전에 미리 알아둘 가치가 있습니다.
내부 구조
이 체크 도구는 무료 JSON 피드(feed)를 읽습니다. API 키도, 가입도 필요 없으며, CORS가 개방되어 있습니다. 또한 모든 행에는 해당 값이 읽혀온 제공업체(provider) 페이지 정보가 포함되어 있습니다:
curl -s https://aimodelwatch.dev/api/models.json | head -20
curl -s https://aimodelwatch.dev/api/deprecations.json | head -20
그 안의 모든 내용은 제공업체가 자체 문서에서 명시한 사항입니다. 제공업체가 아무것도 게시하지 않은 경우, 해당 필드는 null로 처리되며 시스템은 추측하는 대신 조용히 검사를 건너뜁니다. 누군가의 빌드를 실패하게 만드는 임의로 만들어낸 은퇴 날짜(retirement date)는 정보가 누락된 것보다 훨씬 더 나쁜 결과를 초래할 것이기 때문입니다.
파이프라인(pipeline)에 서드파티 액션(third-party action)을 추가하고 싶지 않다면, 의존성이 없는 단일 파일로 구성된 스크립트를 사용할 수 있으며 모든 CI에서 작동합니다. curl로 가져와서 바로 실행하면 됩니다.
어떤 방식을 선택하든: 이 검사에 드는 비용은 약 두 줄의 코드뿐이며, 이를 통해 잡아낼 수 있는 것은 테스트가 통과(green)되는 동안 갑자기 존재하지 않게 되는 모델입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기