AIKit — 왜 만들었나
문제 인식
2026년 현재 AI API를 쓰는 프로젝트를 여럿 운영하고 있다. 퍼즐 게임의 힌트 생성에 OpenAI, 블로그 초안 작성에 Claude, 간단한 분류 작업에 Gemini. 프로젝트마다 AI를 붙이는 건 좋은데, 매번 같은 코드를 다르게 작성하는 게 문제였다.
문제 1: API마다 구조가 다르다
세 프로바이더의 API 호출 방식을 비교하면 이렇다.
// OpenAI — Authorization 헤더, messages 배열
const response = await fetch('https://api.openai.com/v1/chat/completions', {
headers: { 'Authorization': `Bearer ${apiKey}` },
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: message }]
})
});
const text = data.choices[0].message.content;
// Claude — x-api-key 헤더, anthropic-version 필수, max_tokens 필수
const response = await fetch('https://api.anthropic.com/v1/messages', {
headers: {
'x-api-key': apiKey,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1024, // ← 이거 빠뜨리면 에러
messages: [{ role: 'user', content: message }]
})
});
const text = data.content[0].text;
// Gemini — API 키를 URL에 넣음, contents/parts 구조
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`;
const response = await fetch(url, {
body: JSON.stringify({
contents: [{ parts: [{ text: message }] }]
})
});
const text = data.candidates[0].content.parts[0].text;
인증 방식, 요청 구조, 응답 파싱 전부 다르다. 프로젝트 3개면 이 코드가 3번 중복된다.
문제 2: 프로바이더 전환 비용
OpenAI가 요금을 올리거나, Claude가 새 모델을 출시하거나, Gemini가 무료 티어를 확대할 때. 프로바이더를 바꾸려면:
- API 호출 코드 수정
- 응답 파싱 로직 수정
- 에러 핸들링 수정
- 전체 재테스트
한 프로젝트에서 이 작업을 했는데 2일이 걸렸다. 벤더 락인이다.
문제 3: 비용 추적 불가
API를 쓰면서 가장 불안한 건 비용이다. OpenAI 대시보드, Anthropic 대시보드, Google 대시보드를 각각 열어서 확인해야 한다. 프로젝트 단위로 "지금까지 얼마 썼는지"를 한눈에 볼 수 없었다.
문제 4: 장애 대응
API가 429(Rate Limit)를 뱉거나 서버가 다운되면? 수동으로 다른 프로바이더로 전환해야 한다. 자동 폴백이 없다.
해결: AIKit
이 4가지 문제를 한 번에 해결하는 라이브러리를 만들었다.
// Before — 프로바이더마다 다른 코드
const openaiResponse = await callOpenAI(message);
const claudeResponse = await callClaude(message);
const geminiResponse = await callGemini(message);
// After — 하나의 인터페이스
const ai = new AIKit({ provider: 'openai', apiKey: 'sk-...' });
const response = await ai.chat('안녕하세요');
console.log(response.content); // 동일한 응답 구조
프로바이더를 바꾸고 싶으면 provider: 'claude'로 한 줄만 수정하면 된다.
핵심 기능
| 기능 | 설명 |
|---|---|
| 통합 인터페이스 | OpenAI, Claude, Gemini를 동일한 chat() 메서드로 호출 |
| 자동 폴백 | 프로바이더 장애 시 다음 순위로 자동 전환 |
| 비용 추적 | 프로바이더별, 모델별 실시간 비용 계산 |
| 스마트 캐싱 | 동일 질문에 대한 응답 재사용 (LocalStorage 기반) |
| 응답 검증 | QA 관점의 응답 유효성 검사 |
자동 폴백 예시
const ai = new AIKit({
autoFallback: true,
providers: [
{ name: 'openai', apiKey: 'sk-...', priority: 1 },
{ name: 'claude', apiKey: 'sk-ant-...', priority: 2 },
{ name: 'gemini', apiKey: 'AIza...', priority: 3 }
]
});
// OpenAI 장애 → 자동으로 Claude 시도 → 그것도 실패하면 Gemini
const response = await ai.chat('분석해줘');
왜 순수 JavaScript인가
TypeScript가 아니라 순수 JavaScript를 선택한 이유:
| 항목 | JavaScript | TypeScript |
|---|---|---|
| 빌드 도구 | 불필요 | 필수 (tsc) |
| 브라우저 호환 | 즉시 실행 | 빌드 후 실행 |
| CDN 배포 | <script> 한 줄 |
번들링 필요 |
| 의존성 | 0개 | @types 등 필요 |
| 진입장벽 | 낮음 | 중간 |
CDN으로 <script> 한 줄이면 바로 쓸 수 있는 라이브러리를 목표로 했다. npm 설치도 가능하지만, 가장 빠른 시작은 CDN이다.
Before / After
| 항목 | Before | After (AIKit) |
|---|---|---|
| 프로바이더 전환 | 2일 | 1줄 수정 |
| 코드 중복 | 프로바이더당 ~100줄 | chat() 한 번 |
| 비용 확인 | 각 대시보드 개별 확인 | getCostReport() |
| 장애 대응 | 수동 전환 | 자동 폴백 |
현재 상태
- 버전: v1.1.1
- 빌드: ESM + UMD + Minified (Rollup)
- 테스트: 45개 통과, 커버리지 87%
- 의존성: 0개 (런타임)
- GitHub: github.com/lukaPlayground/aikit
마무리
이 글은 AIKit 시리즈 8편 중 첫 번째다. 다음 편에서는 Adapter Pattern을 이용한 아키텍처 설계를 다룬다.
기술 스택: Vanilla JavaScript, Rollup, Jest
소스 코드: GitHub
라이브 데모: AIKit Playground
'프로젝트 > AIKit' 카테고리의 다른 글
| AIKit #6 - React & Vue에서 AIKit 사용하기 (0) | 2026.02.08 |
|---|---|
| AIKit #5 - PHP 프로젝트에서 AIKit 연동하기 (0) | 2026.02.08 |
| AIKit #4 - 5분 만에 AI 챗봇 만들기 (0) | 2026.02.07 |
| AIKit #3 - QA 개발자가 만든 AI 라이브러리 (0) | 2026.02.07 |
| AIKit #2 - Adapter Pattern으로 멀티 프로바이더 지원하기 (0) | 2026.02.06 |