claude-lib에 GitHub Copilot 프로바이더 추가하기 — 단순 작업은 GPT-5-mini로, 비용을 절반으로
배경: 모든 작업에 Claude를 쓸 필요는 없다
claude-lib는 홈서버 내 여러 프로젝트에서 Claude Code CLI 연동 로직을 공용으로 묶은 라이브러리입니다. 구조는 단순합니다. 프로젝트가 함수를 호출하면 claude-lib가 child_process.spawn으로 CLI를 실행하고, 결과를 반환합니다.
프로젝트 (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 필드를 추가하는 방식을 선택했습니다.
// 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로 분기합니다.
// 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 호출에는 두 종류의 토큰이 필요합니다.
// 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의 기존 원칙(외부 의존성 최소화)을 그대로 유지한 것입니다.
// 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 패턴을 유지했습니다.
// index.ts — re-export 추가
export { executeCopilot } from './copilot-executor.js';
export { getCopilotToken } from './copilot-auth.js';
export type { Provider } from './types.js';
기존 프로젝트에서의 사용 방식은 전혀 변하지 않습니다.
// 기존 코드 — 변경 없이 동작
import { executeOneShot } from 'claude-lib';
const result = await executeOneShot({ prompt: '복잡한 분석 작업...' });
// 단순 작업에 GPT-5-mini 사용
const result = await executeOneShot({
prompt: '이 뉴스의 카테고리를 분류하세요.',
provider: 'copilot',
});
실제 적용 예시
대화 로그에서 볼 수 있듯, 실제로 아래와 같은 작업들이 GPT-5-mini로 전환하기 좋은 후보입니다.
| 작업 | 기존 | 전환 후 | 이유 |
|---|---|---|---|
| 뉴스 카테고리 분류 | Claude | GPT-5-mini | 4개 중 1개 선택, 단순 분류 |
| 영문 기사 번역 | Claude | GPT-5-mini | 정형화된 번역 작업 |
| 뉴스 피처 추출 (JSON) | Claude | GPT-5-mini | 구조화된 출력 생성 |
| 유튜브 영상 분류 | Claude | GPT-5-mini | 키워드 기반 분류 |
| 코드 리뷰, 아키텍처 설계 | Claude | Claude 유지 | 복잡한 추론 필요 |
핵심 정리
하위 호환성을 깨지 않는 확장이 핵심입니다. 멀티 프로바이더를 추가할 때 기억할 점을 정리합니다.
- 기존 API 시그니처를 유지하라 — 옵셔널 필드 추가로 분기하면 기존 코드는 한 줄도 바꿀 필요 없다
- 라우팅은 진입점 한 곳에서 —
executor.ts한 파일에서 provider별 분기를 처리하면 로직이 흩어지지 않는다 - 외부 의존성을 늘리지 마라 —
fetch로 충분한 일에 SDK를 추가하면 유지보수 부담만 늘어난다 - 토큰 캐싱은 필수 — Copilot 세션 토큰은 만료 시간이 있으므로 매 요청마다 발급받으면 레이턴시가 늘어난다
- 비용 분기 기준을 명확히 하라 — "추론이 필요한가?"를 기준으로 나누면 대부분의 작업을 분류할 수 있다
GitHub Copilot Pro $10/월 구독 하나로 단순 작업의 API 비용을 0으로 만들 수 있습니다. 복잡한 작업만 Claude에 남기면, 전체 API 비용은 작업 분포에 따라 40~60% 절감할 수 있습니다.