GitHub Models 종료: 초보자가 AI 벤더 종속 (Vendor Lock-In)에 대해 배워야 할 점
요약
GitHub Models의 서비스 종료 소식을 통해 AI 벤더 종속(Vendor Lock-in)의 위험성을 경고합니다. 특정 AI 서비스의 API나 구조가 애플리케이션 아키텍처에 깊게 침투하지 않도록 설계하는 것이 중요함을 강조합니다.
핵심 포인트
- GitHub Models의 플레이그라운드 및 API 서비스가 종료됨
- AI 서비스는 앱의 형태를 결정하는 것이 아닌 구동 동력이 되어야 함
- 교체 가능한 이음새(replaceable seam)를 설계하여 벤더 종속 방지
- 특정 제공업체의 응답 객체나 프롬프트가 코드 전반에 퍼지지 않도록 주의
AI 기능은 서비스 제공자가 건물에서 간판을 떼어내기 직전까지는 영구적인 것처럼 보일 수 있습니다.
GitHub Models는 7월 30일에 그 순간을 맞이했습니다. GitHub는 플레이그라운드(playground), 모델 카탈로그(model catalog), 추론 API(inference API), 그리고 BYOK(bring-your-own-key) 엔드포인트가 신규 고객뿐만 아니라 기존 고객에게도 모두 은퇴(retired)될 것이라고 발표했습니다.
지난 24시간 동안 이보다 더 중대한 AI 또는 개발 도구 관련 소식을 찾지 못해, 범위를 7일로 넓혔습니다. 종료는 3일 전에 발생했습니다.
GitHub는 모델 접근을 위해 Microsoft Foundry를, GitHub 내부의 AI 워크플로우를 위해 GitHub Copilot을 사용하도록 개발자들을 안내했습니다. 이는 합리적인 마이그레이션(migration) 경로입니다. 하지만 초보자가 얻을 수 있는 유용한 교훈은 어떤 대체제를 선택하느냐보다 더 큰 차원의 문제입니다.
외부 AI 서비스는 기능을 구동하는 동력이 되어야 합니다. 서비스가 앱 전체의 형태를 결정해서는 안 됩니다.
어제 저는 병목 현상(bottleneck)에 맞춰 AI 코딩 모델을 선택하는 방법에 대해 글을 썼습니다. 이것은 그다음 단계의 아키텍처(architectural) 질문입니다. 합리적인 모델이나 서비스 선택이라 할지라도 변경될 수 있고, 더 비싸질 수 있으며, 기능을 상실하거나, 다른 제품으로 이동하거나, 혹은 사라질 수도 있습니다.
이를 대비하기 위해 엔터프라이즈 아키텍처 팀이 필요한 것은 아닙니다. 여러분에게 필요한 것은 단 하나의 교체 가능한 이음새(replaceable seam)입니다.
실제로 무엇이 변했나
GitHub의 은퇴 공지는 이례적으로 구체적이었습니다. 7월 30일 이후에는 BYOK를 포함한 GitHub Models 인터페이스와 API를 더 이상 사용할 수 없게 됩니다. GitHub는 심지어 은퇴 전에 짧은 서비스 중단(service interruptions)을 실행하여 개발자들이 장애가 어떤 모습인지 확인할 수 있도록 했습니다.
이 마지막 세부 사항이 중요합니다. 브라운아웃(brownout, 일시적 서비스 중단)은 단순한 불편함이 아닙니다. 그것은 아키텍처 테스트입니다.
만약 하나의 AI 엔드포인트(endpoint)가 실패했을 때 앱 전체를 사용할 수 없게 된다면, 그 앱은 아마도 해당 엔드포인트에 대해 너무 많은 것을 알고 있는 것입니다.
결제, 지도, 이메일, 분석 (Analytics), 스토리지 (Storage), 인증 (Authentication)에서도 동일한 문제가 발생합니다. AI 서비스는 첫 번째 통합 과정이 매우 빠르기 때문에 이를 무시하기가 더 쉽습니다. 패키지를 설치하고, 키를 붙여넣고, 화면에서 모델을 호출한 뒤, 텍스트가 나타나면 축하를 합니다.
그러면 프로토타입 (Prototype)이 제품이 되고, 지름길은 구조적 강철 (Structural steel)이 되어 버립니다.
초보자들이 벤더 종속 (Vendor lock-in)에 대해 자주 오해하는 것
벤더 종속 (Vendor lock-in)은 "업체를 사용하는 것"을 의미하지 않습니다. 모든 유용한 앱은 타인의 소프트웨어에 의존합니다.
문제는 제공업체 특유의 세부 사항이 도처에 퍼질 때 시작됩니다:
- 모델 이름이 UI 컴포넌트 (UI components) 내부에 존재함
- API 호출이 여러 화면에 걸쳐 복사되어 있음
- 제공업체의 응답 객체 (Response objects)가 앱의 데이터 모델 (Data model)이 됨
- 에러 메시지가 사용자에게 직접 표시됨
- 프롬프트 (Prompts)가 버튼 핸들러 (Button handlers)에 뒤섞여 있음
- 해당 기능이 무엇을 반환해야 하는지에 대한 기록이 없음
이제 제공업체를 변경하는 것은 단 하나의 통합 작업이 아닙니다. 그것은 고고학적 발굴 작업입니다.
반대의 극단 또한 실수입니다. 초보자는 단 한 명의 사용자가 해당 기능을 원하는지 증명하기도 전에, 거창한 멀티 프로바이더 (Multi-provider) 프레임워크를 구축하는 데 2주를 소비할 수 있습니다. 그것은 아키텍처 코스프레 (Architecture cosplay)입니다.
목표는 더 작습니다: 불안정한 의존성 (Dependency)을 가장 좁고 유용한 계약 (Contract) 뒤로 배치하는 것입니다.
만약 당신이 여전히 거친 앱 아이디어를 구축 가능한 워크플로우 (Workflow)로 만드는 과정에 있다면, AI 서비스를 선택하기 전에 사용자, 결과, 입력, 제약 조건 및 증명을 정의할 수 있도록 AI App Builder Starter Prompts를 무료로 만들었습니다.
단일 이음매 워크플로우 (The one-seam workflow)
긴 노트를 세 개의 실행 항목 (Action items)으로 변환하는 기능이 있는 노트 앱을 만든다고 가정해 봅시다.
사용자 여정 (User journey)은 간단합니다:
- 사용자가 저장된 노트를 엽니다.
- 사용자가 "실행 항목 찾기"를 누릅니다.
- 앱이 0개에서 3개의 짧은 실행 항목을 반환합니다.
- 사용자는 각 항목을 수락, 편집 또는 폐기할 수 있습니다.
- 사용자가 확인하기 전까지 원본 노트는 절대 변경되지 않습니다.
그것이 바로 제품 계약 (product contract)입니다. 이 중 그 어떤 것도 화면(screen)이 제공자(provider), 모델 이름, SDK 또는 원시 응답 형식 (raw response format)을 알 필요로 하지 않습니다.
통합 구조를 교체 가능한 상태로 유지하는 방법은 다음과 같습니다.
1. 제품 언어로 기능을 명명하세요
extractActionItems(noteText)와 같은 방식으로 이름을 붙이세요.
메인 함수의 이름을 특정 벤더 (vendor)의 이름을 따서 짓지 마세요. 앱의 나머지 부분은 결과물에 관심을 가질 뿐, 어떤 회사가 그것을 만들었는지에는 관심이 없습니다.
2. 자신만의 입출력을 정의하세요
입력값은 다음과 같을 수 있습니다:
- 노트 텍스트 (note text)
- 항목의 최대 개수
- 언어
출력값은 다음과 같을 수 있습니다:
- 실행 항목 (action-item) 문자열 배열
complete,empty또는unavailable과 같은 상태 값- UI에 표시할 수 있는 안전한 메시지
앱은 화면에 전달되기 전에 해당 결과를 검증해야 합니다. 제공자의 응답 객체 (response object)는 경계(border)에 도착한 증거일 뿐, 여러분의 내부 규정 (internal constitution)이 아닙니다.
3. 제공자 호출을 하나의 어댑터 (adapter)에 담으세요
하나의 서버 라우트 (server route), 서비스 파일 또는 백엔드 함수가 제공자 SDK와 모델 식별자 (model identifier)를 소유하도록 합니다.
UI는 여러분의 기능을 호출합니다. 여러분의 기능은 어댑터를 호출합니다. 어댑터는 외부 응답을 여러분의 출력 형태 (output shape)로 변환합니다.
그것이 바로 접합부 (seam)입니다.
Vercel의 AI SDK 문서는 표준화된 언어 모델 인터페이스 (language-model interface)를 통해 이와 동일한 광범위한 개념을 설명합니다. 해당 문서의 provider-management 가이드는 중앙 레지스트리 (central registry), 별칭 (aliases) 및 여러 제공자를 통해 더 나아가 설명합니다. 반드시 해당 라이브러리를 사용할 필요는 없습니다. 중요한 설계 교훈은 중앙 집중화 (centralization)입니다. 교체 작업은 알려진 단 한 곳에서 이루어져야 합니다.
4. 비밀 키와 모델 ID를 클라이언트로부터 격리하세요
브라우저나 모바일 앱에 제공자 비밀 키 (provider secrets)를 포함하여 배포하지 마세요. 이를 서버나 보안이 유지되는 백엔드 함수에 보관하세요.
또한, 선택한 제공자와 모델을 기능 코드 곳곳에 흩뿌려 놓지 말고 설정 (configuration) 파일에 유지하세요. 모델을 변경할 때 다섯 개의 버튼과 세 개의 화면을 수정해야 하는 상황이 발생해서는 안 됩니다.
5. 세 개의 피스처 (fixtures)를 저장하세요
해당 피스처 (feature)를 대표하는 세 가지 작은 예시를 유지하세요:
- 두 개의 명확한 실행 항목 (action items)이 포함된 일반적인 노트
- 실행 항목이 없는 노트
- 모델이 실행 항목을 임의로 만들어내도록 유혹할 수 있는 지저분한 노트
각 피스처 (fixture)에 대해 허용 가능한 결과값을 작성하세요. 프롬프트 (prompt), 모델 (model), 또는 제공자 (provider)를 변경할 때마다 동일한 피스처 (fixtures)를 다시 실행하십시오.
이것은 완벽한 벤치마크 (benchmark)는 아닙니다. 이는 제품 특화된 마이그레이션 테스트 (migration test)입니다.
6. 사용 불가능한 상태 (unavailable state)를 설계하세요
제공자 (provider)가 타임아웃 (timeout)되거나, 요청을 거부하거나, 한도에 도달하거나, 혹은 사라졌을 때 사용자가 무엇을 보게 될지 결정하세요.
노트 앱의 경우, 제품의 나머지 부분은 여전히 작동해야 합니다. 사용자는 노트를 읽고 편집할 수 있어야 합니다. 실행 항목 (action-item) 기능은 데이터를 손상시키거나 사용자를 스피너 (spinner) 뒤에 가두지 않으면서, 일시적으로 사용할 수 없다고 안내할 수 있어야 합니다.
GitHub의 서비스 종료 전 브라운아웃 (brownouts)은 좋은 교훈을 줍니다. 타이밍을 직접 제어할 수 있을 때 실패 상황을 테스트하십시오.
7. 한 페이지 분량의 종료 노트 (exit note)를 작성하세요
다음 내용을 기록하세요:
- 제공자 (provider)가 호출되는 위치
- 필요한 환경 변수 (environment variables)
- 입력 및 출력 규약 (input and output contract)
- 세 가지 마이그레이션 피스처 (migration fixtures)
- 의존하고 있는 제공자 특화 기능 (provider-specific features)
- 장애 발생 시 사용자가 경험하게 되는 것
해당 노트만 있다면, 향후 마이그레이션 (migration)을 시작할 때 "이게 대체 어디에 연결되어 있는 거지?"라는 질문으로 시작하는 상황을 방지하기에 충분합니다.
실질적인 제공자 종료 테스트 (provider-exit test)
AI 코딩 도구에게 코드를 수정하지 않고 프로젝트를 조사하여 다음 질문에 답하도록 요청할 수 있습니다:
- 앱이 AI 제공자 (AI provider)를 호출하는 곳은 어디인가?
- 얼마나 많은 파일에 제공자 (provider) 또는 모델 (model) 이름이 포함되어 있는가?
- UI가 제공자 SDK를 직접 임포트 (import)하는가?
- 제공자의 출력이 검증 없이 저장된 앱 데이터로 흘러 들어가는가?
- 모든 AI 요청이 한 시간 동안 실패한다면 무엇이 여전히 작동하는가?
- 의존성 (dependency)을 격리할 수 있는 가장 작은 경계 (boundary)는 무엇인가?
- 교체된 모델이 충분히 잘 작동함을 증명할 세 가지 피스처 (fixtures)는 무엇인가?
그다음, 한 가지 제약 조건을 걸어 마이그레이션 계획 (migration plan)을 요청하세요: 사용자 여정 (user journey)을 보존하고, 사용자에게 노출되는 파일의 변경을 최소화할 것.
무료 AI 앱 빌더 스타터 프롬프트를 사용하면 도구가 코드를 이동시키기 전에 해당 워크플로 (workflow)와 그 증명 (proof)을 정의하는 데 도움을 받을 수 있습니다.
트레이드오프 (tradeoff): 추상화는 유용한 차이점을 숨길 수 있습니다
공급자 경계 (provider boundary)는 공짜가 아닙니다.
서로 다른 모델과 서비스는 서로 다른 도구, 컨텍스트 크기 (context sizes), 구조화된 출력 (structured-output) 동작, 안전 제어 (safety controls), 지연 시간 (latency), 그리고 가격 책정 (pricing)을 지원합니다. 만약 모든 공급자를 최소 공통 분모 (lowest common denominator)에 맞추도록 강제한다면, 당신이 처음 선택했던 모델을 가치 있게 만들었던 기능을 잃을 수 있습니다.
그렇기 때문에 저는 모든 공급자가 동일하다고 가장하지 않을 것입니다.
제품 계약 (product contract)은 안정적으로 유지하되, 어댑터 (adapter) 내부에서는 공급자별 설정 (provider-specific settings)을 허용하세요. 만약 특정 공급자가 유용한 기능을 지원한다면, 이를 의도적으로 사용하고 종료 노트 (exit note)에 기록해 두세요. 당신의 대체 모델이 사용자의 결과물 (user outcome)을 보존하는 한, 다른 구현 방식이 필요할 수도 있습니다.
라우팅 레이어 (Routing layers) 또한 도움이 될 수 있지만, 이는 또 다른 의존성 (dependency)을 도입합니다. 예를 들어, Vercel의 AI Gateway 라우팅 규칙은 애플리케이션 코드 변경 없이 한 모델에서 다른 모델로 요청을 재작성할 수 있습니다. 이는 복구 능력을 향상시킬 수 있지만, 목적지가 여전히 수용 가능한 결과를 생성하는지 확인해야 하는 당신의 책임을 제거해주지는 않습니다.
규칙은 "아무것에도 의존하지 마라"가 아닙니다.
"의존성이 어디서 끝나고 당신의 제품이 어디서 시작되는지 알라"입니다.
다음에 해야 할 일
당신의 앱에서 외부 서비스 하나를 선택하세요. AI, 이메일, 결제, 지도, 또는 스토리지 (storage) 무엇이든 좋습니다.
세 개의 상자를 그리세요:
당신의 화면 -> 당신의 기능 -> 외부 공급자
만약 공급자의 세부 사항이 도처에 나타나서 이러한 경계를 그릴 수 없다면, 오늘 밤 프로젝트 전체를 다시 작성하지 마세요. 하나의 사용자 워크플로 (user workflow)를 선택하고, 그 입력 (input)과 출력 (output)을 작성한 뒤, 외부 호출을 하나의 이음매 (seam) 뒤로 옮기고, 세 개의 픽스처 (fixtures)를 저장하세요.
그것만으로도 첫 번째 마이그레이션 계획 (migration plan)을 위한 아키텍처 (architecture)로는 충분합니다.
즉각적인 가이드가 포함된 실행을 원하신다면, 무료로 제공되는 AI App Builder Starter Prompts로 시작해 보세요. 아이디어 단계부터 출시까지 체계적인 경로를 원하신다면, 범위 (scope), 스택 (stack), 아키텍처 (architecture), 프롬프팅 (prompting), QA, 배포 (deployment) 및 출시 (launch)를 다루는 저의 19달러짜리 현장 매뉴얼인 AI App Builder From Zero를 확인해 보시기 바랍니다.
또한 저를 다음 채널에서도 만나보실 수 있습니다:
Medium: https://medium.com/@marcusykim
DEV.to: https://dev.to/marcusykim
Website: https://marcusykim.com/
X: https://x.com/marcusykim
LinkedIn: https://www.linkedin.com/in/marcusykim/
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기