2026.02.13AI 개발 패턴
claude-libgithub-copilotgpt-5-minimulti-providercost-optimization

claude-lib에 GitHub Copilot 프로바이더 추가하기 — 단순 작업은 GPT-5-mini로, 비용을 절반으로

배경: 모든 작업에 Claude를 쓸 필요는 없다

claude-lib는 홈서버 내 여러 프로젝트에서 Claude Code CLI 연동 로직을 공용으로 묶은 라이브러리입니다. 구조는 단순합니다. 프로젝트가 함수를 호출하면 claude-lib가 child_process.spawn으로 CLI를 실행하고, 결과를 반환합니다.

code
프로젝트 (StockOne, DevTeam, YTK-Note)
    ↓ 함수 호출
claude-lib
    ↓ child_process.spawn
Claude Code CLI (~/.local/bin/claude)
    ↓
Anthropic API

문제는 비용이었습니다. 뉴스 카테고리 분류, 단순 번역, JSON 포맷 변환처럼 복잡한 추론이 필요 없는 작업에도 Claude API 토큰을 소모하고 있었습니다. GitHub Copilot Pro 구독($10/월)으로 GPT-5-mini를 무제한 사용할 수 있는 상황에서, 단순 작업을 GPT-5-mini로 돌리면 Claude API 비용을 줄일 수 있다는 계산이 나왔습니다.

설계: 멀티 프로바이더 아키텍처

핵심 제약은 하위 호환성이었습니다. claude-lib를 이미 사용 중인 프로젝트들이 코드 변경 없이 그대로 동작해야 합니다.

멀티 프로바이더 아키텍처 다이어그램

라우팅 분기 설계

기존 executeOneShot 함수의 시그니처를 유지하되, 옵션에 provider 필드를 추가하는 방식을 선택했습니다.

typescript
// types.ts — provider 타입 추가
export type Provider = 'claude' | 'copilot';

export interface OneShotOptions {
  prompt: string;
  systemPrompt?: string;
  provider?: Provider;  // 기본값: 'claude' (하위 호환)
  // ... 기존 필드 유지
}

provider를 지정하지 않으면 기존처럼 Claude CLI를 사용합니다. provider: 'copilot'을 명시하면 GitHub Copilot API로 분기합니다.

typescript
// executor.ts — 라우팅 분기
export async function executeOneShot(options: OneShotOptions) {
  const provider = options.provider ?? 'claude';

  if (provider === 'copilot') {
    return executeCopilot(options);
  }
  return executeClaude(options);  // 기존 로직 그대로
}

이렇게 하면 기존 프로젝트는 코드 한 줄 바꾸지 않아도 되고, GPT-5-mini를 쓰고 싶은 곳에서만 provider: 'copilot'을 추가하면 됩니다.

구현: Copilot 토큰 인증 흐름

GitHub Copilot API는 공개 문서화된 REST API가 아닙니다. OpenClaw 등 오픈소스 프로젝트에서 리버스 엔지니어링한 내부 API 호출 방식을 참고하여 직접 구현했습니다.

2단계 토큰 인증

Copilot API 호출에는 두 종류의 토큰이 필요합니다.

typescript
// copilot-auth.ts

// 1단계: GitHub Personal Access Token으로 Copilot 세션 토큰 발급
async function getCopilotToken(githubToken: string): Promise<CopilotToken> {
  const response = await fetch(
    'https://api.github.com/copilot_internal/v2/token',
    {
      headers: {
        'Authorization': `token ${githubToken}`,
        'Accept': 'application/json',
      },
    }
  );
  const data = await response.json();
  return {
    token: data.token,
    expiresAt: data.expires_at,  // 보통 30분
  };
}

// 2단계: 세션 토큰으로 Chat Completions API 호출
async function callCopilotChat(
  copilotToken: string,
  messages: Message[]
): Promise<string> {
  const response = await fetch(
    'https://api.githubcopilot.com/chat/completions',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${copilotToken}`,
        'Content-Type': 'application/json',
        'Editor-Version': 'vscode/1.96.0',
      },
      body: JSON.stringify({
        model: 'gpt-5-mini',
        messages,
      }),
    }
  );
  const data = await response.json();
  return data.choices[0].message.content;
}

GitHub PAT(Personal Access Token)로 Copilot 내부 토큰을 발급받고, 이 토큰으로 실제 Chat API를 호출하는 2단계 구조입니다. 세션 토큰은 약 30분 후 만료되므로 캐싱과 자동 갱신 로직도 함께 구현했습니다.

외부 의존성 제로

openai SDK나 axios 같은 외부 패키지 없이 Node.js 18+ 내장 fetch API만 사용했습니다. claude-lib의 기존 원칙(외부 의존성 최소화)을 그대로 유지한 것입니다.

typescript
// copilot-executor.ts
export async function executeCopilot(
  options: OneShotOptions
): Promise<OneShotResult> {
  const githubToken = getConfig().githubToken;
  const copilotToken = await getOrRefreshCopilotToken(githubToken);

  const messages = [];
  if (options.systemPrompt) {
    messages.push({ role: 'system', content: options.systemPrompt });
  }
  messages.push({ role: 'user', content: options.prompt });

  const content = await callCopilotChat(copilotToken, messages);

  return {
    result: content,
    costUsd: 0,  // Copilot Pro 구독 내 무제한
    provider: 'copilot',
  };
}

코드 에디터에서 작업하는 모습

빌드와 배포

구현 후 빌드 및 타입 체크를 통과시키고, index.ts에서 새 모듈들을 re-export하여 기존 import 패턴을 유지했습니다.

typescript
// index.ts — re-export 추가
export { executeCopilot } from './copilot-executor.js';
export { getCopilotToken } from './copilot-auth.js';
export type { Provider } from './types.js';

기존 프로젝트에서의 사용 방식은 전혀 변하지 않습니다.

typescript
// 기존 코드 — 변경 없이 동작
import { executeOneShot } from 'claude-lib';
const result = await executeOneShot({ prompt: '복잡한 분석 작업...' });

// 단순 작업에 GPT-5-mini 사용
const result = await executeOneShot({
  prompt: '이 뉴스의 카테고리를 분류하세요.',
  provider: 'copilot',
});

실제 적용 예시

대화 로그에서 볼 수 있듯, 실제로 아래와 같은 작업들이 GPT-5-mini로 전환하기 좋은 후보입니다.

작업기존전환 후이유
뉴스 카테고리 분류ClaudeGPT-5-mini4개 중 1개 선택, 단순 분류
영문 기사 번역ClaudeGPT-5-mini정형화된 번역 작업
뉴스 피처 추출 (JSON)ClaudeGPT-5-mini구조화된 출력 생성
유튜브 영상 분류ClaudeGPT-5-mini키워드 기반 분류
코드 리뷰, 아키텍처 설계ClaudeClaude 유지복잡한 추론 필요

핵심 정리

하위 호환성을 깨지 않는 확장이 핵심입니다. 멀티 프로바이더를 추가할 때 기억할 점을 정리합니다.

  1. 기존 API 시그니처를 유지하라 — 옵셔널 필드 추가로 분기하면 기존 코드는 한 줄도 바꿀 필요 없다
  2. 라우팅은 진입점 한 곳에서executor.ts 한 파일에서 provider별 분기를 처리하면 로직이 흩어지지 않는다
  3. 외부 의존성을 늘리지 마라fetch로 충분한 일에 SDK를 추가하면 유지보수 부담만 늘어난다
  4. 토큰 캐싱은 필수 — Copilot 세션 토큰은 만료 시간이 있으므로 매 요청마다 발급받으면 레이턴시가 늘어난다
  5. 비용 분기 기준을 명확히 하라 — "추론이 필요한가?"를 기준으로 나누면 대부분의 작업을 분류할 수 있다

GitHub Copilot Pro $10/월 구독 하나로 단순 작업의 API 비용을 0으로 만들 수 있습니다. 복잡한 작업만 Claude에 남기면, 전체 API 비용은 작업 분포에 따라 40~60% 절감할 수 있습니다.