브라우저에서 바로 AI를 활용하고 싶으신가요? 오늘은 Chrome 확장 프로그램으로 HolySheep AI를 연결하는 방법을 초보자도 이해할 수 있도록 단계별로 알려드리겠습니다.

📌 먼저 알아두실 점: HolySheep AI는 지금 가입하면 무료 크레딧을 제공하며, 단일 API 키로 GPT-4.1, Claude, Gemini, DeepSeek 등 모든 주요 모델을 사용할 수 있습니다. 로컬 결제가 지원되어 해외 신용카드 없이도 개발자 친화적으로 시작할 수 있습니다.

왜 Chrome Extension인가?

저는日常 업무에서 웹페이지 텍스트를 번역하거나, 이메일 drafts를 작성할 때마다 새로운 탭을 열어야 하는 번거로움에 지쳐있었습니다. Chrome Extension을 만들면:

1단계: 프로젝트 구조 설정

먼저 프로젝트 폴더를 만들고 아래 구조를 만드세요:

my-ai-extension/
├── manifest.json
├── background.js
├── content.js
├── popup.html
├── popup.js
├── styles.css
├── icon.png (128x128 PNG)
└── icon-small.png (16x16 PNG)

2단계: Manifest V3 설정

manifest.json 파일을 작성합니다. Manifest V3는 Chrome 확장 프로그램의 최신 버전으로, 보안과 성능이 크게 개선되었습니다.

{
  "manifest_version": 3,
  "name": "HolySheep AI Assistant",
  "version": "1.0.0",
  "description": "브라우저에서 바로 HolySheep AI를 활용하세요",
  "permissions": [
    "activeTab",
    "contextMenus",
    "storage"
  ],
  "host_permissions": [
    "https://api.holysheep.ai/*"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icon-small.png",
      "128": "icon.png"
    }
  },
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": [""],
      "js": ["content.js"]
    }
  ],
  "icons": {
    "16": "icon-small.png",
    "128": "icon.png"
  }
}
💡 팁: host_permissionshttps://api.holysheep.ai/*를 반드시 추가해야 합니다. 그렇지 않으면 API 호출 시 CORS 오류가 발생합니다.

3단계: API 설정 및 응답 처리

Extension의 핵심인 background.js를 작성합니다. HolySheep AI의 게이트웨이 엔드포인트를 사용합니다.

// HolySheep AI API 설정
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';
const API_KEY = 'YOUR_HOLYSHEEP_API_KEY';

// 메시지 수신 대기
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'callAI') {
    callHolySheepAPI(request.prompt, request.selectedText)
      .then(response => sendResponse({ success: true, data: response }))
      .catch(error => sendResponse({ success: false, error: error.message }));
    return true; // 비동기 응답을 위해 필요
  }
});

// HolySheep AI API 호출 함수
async function callHolySheepAPI(prompt, selectedText = '') {
  const fullPrompt = selectedText 
    ? 선택된 텍스트: "${selectedText}"\n\n요청: ${prompt}
    : prompt;

  const response = await fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': Bearer ${API_KEY}
    },
    body: JSON.stringify({
      model: 'gpt-4.1',  // HolySheep AI에서 지원되는 모델
      messages: [
        {
          role: 'user',
          content: fullPrompt
        }
      ],
      max_tokens: 1000,
      temperature: 0.7
    })
  });

  if (!response.ok) {
    const errorData = await response.json().catch(() => ({}));
    throw new Error(API 오류: ${response.status} - ${errorData.error?.message || response.statusText});
  }

  const data = await response.json();
  return data.choices[0].message.content;
}

// Context Menu 설정
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: 'aiAssistant',
    title: '🤖 HolySheep AI로 번역하기',
    contexts: ['selection']
  });
  
  chrome.contextMenus.create({
    id: 'aiAssistantSummarize',
    title: '📝 HolySheep AI로 요약하기',
    contexts: ['selection']
  });
});

// Context Menu 클릭 핸들러
chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === 'aiAssistant') {
    chrome.tabs.sendMessage(tab.id, {
      action: 'showPopup',
      text: info.selectionText,
      mode: 'translate'
    });
  } else if (info.menuItemId === 'aiAssistantSummarize') {
    chrome.tabs.sendMessage(tab.id, {
      action: 'showPopup',
      text: info.selectionText,
      mode: 'summarize'
    });
  }
});
💰 비용 참고: HolySheep AI의 GPT-4.1은 $8/MTok, Claude Sonnet 4.5는 $15/MTok, Gemini 2.5 Flash는 $2.50/MTok, DeepSeek V3.2는 $0.42/MTok입니다. 비용 최적화를 위해 텍스트 양에 맞는 적절한 모델을 선택하세요.

4단계: Popup UI 만들기

popup.html으로 사용자가 직접 입력할 수 있는 인터페이스를 만듭니다.




  
  HolySheep AI Assistant
  


  

🐑 HolySheep AI

브라우저 AI 어시스턴트

5단계: Popup 로직 구현

popup.js로 UI와 HolySheep AI 간의 통신을 처리합니다.

document.addEventListener('DOMContentLoaded', () => {
  const promptInput = document.getElementById('prompt');
  const modelSelect = document.getElementById('model');
  const sendBtn = document.getElementById('sendBtn');
  const loading = document.getElementById('loading');
  const responseDiv = document.getElementById('response');
  const responseText = document.getElementById('responseText');
  const tokensInfo = document.getElementById('tokensInfo');
  const errorDiv = document.getElementById('error');
  const errorText = document.getElementById('errorText');

  // 전송 버튼 클릭 핸들러
  sendBtn.addEventListener('click', async () => {
    const prompt = promptInput.value.trim();
    const model = modelSelect.value;

    if (!prompt) {
      showError('질문을 입력해주세요.');
      return;
    }

    // UI 상태 업데이트
    hideAll();
    loading.classList.remove('hidden');
    sendBtn.disabled = true;

    try {
      // Background Script에 메시지 전송
      const response = await chrome.runtime.sendMessage({
        action: 'callAI',
        prompt: prompt,
        selectedText: '',
        model: model
      });

      if (response.success) {
        showResponse(response.data);
      } else {
        showError(response.error);
      }
    } catch (error) {
      showError(연결 오류: ${error.message});
    } finally {
      loading.classList.add('hidden');
      sendBtn.disabled = false;
    }
  });

  function showResponse(text) {
    responseText.textContent = text;
    responseDiv.classList.remove('hidden');
  }

  function showError(message) {
    errorText.textContent = message;
    errorDiv.classList.remove('hidden');
  }

  function hideAll() {
    responseDiv.classList.add('hidden');
    errorDiv.classList.add('hidden');
  }

  // Enter 키로 전송 (Shift+Enter는 줄바꿈)
  promptInput.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      sendBtn.click();
    }
  });
});

6단계: 스타일링

styles.css로 깔끔한 UI를 만들어보세요.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  width: 350px;
  min-height: 400px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: #333;
}

.container {
  padding: 20px;
  background: white;
  min-height: 400px;
  border-radius: 0 0 12px 12px;
}

header {
  text-align: center;
  margin-bottom: 20px;
}

header h1 {
  font-size: 1.5em;
  color: #667eea;
}

.subtitle {
  font-size: 0.85em;
  color: #666;
  margin-top: 4px;
}

.input-section {
  margin-bottom: 15px;
}

label {
  display: block;
  font-size: 0.9em;
  font-weight: 600;
  margin-bottom: 6px;
  color: #444;
}

textarea {
  width: 100%;
  height: 100px;
  padding: 10px;
  border: 2px solid #e0e0e0;
  border-radius: 8px;
  resize: none;
  font-size: 0.9em;
  transition: border-color 0.3s;
}

textarea:focus {
  outline: none;
  border-color: #667eea;
}

.model-select {
  margin-bottom: 15px;
}

select {
  width: 100%;
  padding: 10px;
  border: 2px solid #e0e0e0;
  border-radius: 8px;
  font-size: 0.9em;
  background: white;
}

button {
  width: 100%;
  padding: 12px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 1em;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.2s, opacity 0.2s;
}

button:hover:not(:disabled) {
  transform: translateY(-2px);
}

button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

#loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 20px;
  padding: 15px;
  background: #f5f5f5;
  border-radius: 8px;
}

.spinner {
  width: 20px;
  height: 20px;
  border: 3px solid #e0e0e0;
  border-top-color: #667eea;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

#response {
  margin-top: 15px;
  padding: 15px;
  background: #f0f7ff;
  border-radius: 8px;
  border-left: 4px solid #667eea;
}

#response h3 {
  font-size: 0.9em;
  color: #667eea;
  margin-bottom: 10px;
}

#responseText {
  white-space: pre-wrap;
  line-height: 1.6;
}

.tokens-info {
  margin-top: 10px;
  font-size: 0.8em;
  color: #888;
  text-align: right;
}

.error {
  margin-top: 15px;
  padding: 12px;
  background: #ffe0e0;
  border-radius: 8px;
  color: #d32f2f;
  font-size: 0.9em;
}

.hidden {
  display: none !important;
}

footer {
  padding: 10px;
  text-align: center;
}

footer a {
  color: rgba(255, 255, 255, 0.8);
  text-decoration: none;
  font-size: 0.85em;
}

footer a:hover {
  text-decoration: underline;
}

7단계: Content Script 작성

content.js로 웹페이지에서 선택한 텍스트를 팝업으로 전달합니다.

// 웹페이지에서 메시지 수신
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === 'showPopup') {
    // 선택된 텍스트를 팝업으로 전달
    const popup = chrome.extension.getViews({ type: 'popup' })[0];
    if (popup) {
      popup.postMessage({
        selectedText: message.text,
        mode: message.mode
      }, '*');
    }
  }
});

// 더블 클릭으로 텍스트 선택 감지
document.addEventListener('mouseup', (e) => {
  const selection = window.getSelection();
  const selectedText = selection.toString().trim();
  
  if (selectedText.length > 0) {
    // 선택된 텍스트 정보 저장
    chrome.storage.local.set({ lastSelectedText: selectedText });
  }
});

8단계: Chrome에 로드하고 테스트하기

이제 작성한 Extension을 Chrome에 로드해보겠습니다:

  1. Chrome에서 chrome://extensions 접속
  2. 우측 상단 "Developer mode" 토글 활성화
  3. "Load unpacked" 버튼 클릭
  4. 프로젝트 폴더 선택
  5. Extension 아이콘이 Chrome Toolbar에 나타남
🔧 테스트 방법: 웹페이지에서 텍스트를 선택 후 마우스 우클릭 → "HolySheep AI로 번역하기" 또는 "HolySheep AI로 요약하기" 클릭. 또는 Toolbar 아이콘 클릭하여 직접 질문 입력.

자주 발생하는 오류와 해결책

오류 1: CORS 오류 - "No 'Access-Control-Allow-Origin' header"

이 오류는 API 호출 시 발생하는 교차 출처 리소스 공유 문제입니다.

// ❌ 잘못된 예: 직접 API 호출 (CORS 오류 발생)
const response = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: { 'Authorization': Bearer ${API_KEY} },
  body: JSON.stringify({ ... })
});

// ✅ 올바른 예: HolySheep AI 게이트웨이 사용
const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': Bearer ${API_KEY}
  },
  body: JSON.stringify({ ... })
});

HolySheep AI의 게이트웨이는 이미 CORS 헤더가 설정되어 있어 Extension에서 바로 호출 가능합니다. 반드시 https://api.holysheep.ai/v1 엔드포인트를 사용하세요.

오류 2: Manifest V3 Service Worker 제한

// ❌ 잘못된 예: Service Worker에서 직접 DOM 조작
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  document.querySelector('#result').textContent = request.data; // 오류!
});

// ✅ 올바른 예: Content Script에 위임
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
    chrome.tabs.sendMessage(tabs[0].id, {
      action: 'displayResult',
      data: request.data
    });
  });
});

// Content Script (content.js)에서 DOM 조작
chrome.runtime.onMessage.addListener((message) => {
  if (message.action === 'displayResult') {
    document.getElementById('result').textContent = message.data;
  }
});

Manifest V3에서는 Service Worker가 DOM에 직접 접근할 수 없습니다. 반드시 Content Script를 통해 페이지와 통신해야 합니다.

오류 3: API Key 미설정 또는 유효하지 않음

// ❌ 잘못된 예: 하드코딩된 Key (보안 위험)
const API_KEY = 'sk-xxxx'; // 공개 저장소에 노출!

// ✅ 올바른 예: Storage API로 안전하게 관리
// manifest.json에权限 추가: "storage"

// 설정 저장
chrome.storage.local.set({ apiKey: 'YOUR_HOLYSHEEP_API_KEY' });

// 설정 읽기
async function getApiKey() {
  return new Promise((resolve) => {
    chrome.storage.local.get(['apiKey'], (result) => {
      if (result.apiKey) {
        resolve(result.apiKey);
      } else {
        // 사용자에게 Key 입력 요청
        resolve(prompt('HolySheep AI API Key를 입력하세요:'));
      }
    });
  });
}

// API 호출 시 사용
const API_KEY = await getApiKey();

Extension 코드는 사용자에게 노출되므로 API Key를 하드코딩하지 마세요. HolySheep AI에서 Key를 발급받은 후 지금 가입하여安全管理된 스토리지에 저장하세요.

오류 4: 비동기 응답 처리 누락

// ❌ 잘못된 예: async 콜백에서 응답 즉시 반환
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  fetch('https://api.holysheep.ai/v1/chat/completions', options)
    .then(res => res.json())
    .then(data => sendResponse({ success: true, data }));
  // Promise 완료 전에 응답 반환 → 데이터 누락!
});

// ✅ 올바른 예: return true으로 비동기 응답 활성화
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  fetch('https://api.holysheep.ai/v1/chat/completions', options)
    .then(res => res.json())
    .then(data => sendResponse({ success: true, data }))
    .catch(error => sendResponse({ success: false, error: error.message }));
  
  return true; // 반드시 필요! 비동기 처리 완료를 기다림
});

Chrome Extension의 onMessage 리스너는 동기적으로 동작합니다. 비동기fetch의 결과를 전달하려면 반드시 return true을 추가해야 합니다.

개발 후기

저는 이 Chrome Extension을 만든 뒤日常 업무 효율이 크게 향상되었습니다. 특히 해외 문서를 읽을 때 번거로운 복사-붙여넣기 과정이 사라졌고, 언제든 선택한 텍스트를 AI에게 넘겨 번역·요약·분석할 수 있게 되었습니다.

처음 Manifest V3를 접했을 때 Service Worker의 DOM 접근 제한과 메시지 전달 패턴에戸惑렸지만, Content Script와 Background Script를 적절히 분리하면 생각보다 간단하게 해결할 수 있습니다.

💡 성능 팁: Gemini 2.5 Flash는 $2.50/MTok으로 비용 효율적이면서도 속도가 빠릅니다. 빠른 번역이나 요약에는 Flash 모델, 복잡한 분석에는 GPT-4.1을 추천드립니다.

다음 단계


이 튜토리얼이 도움이 되셨나요? HolySheep AI는 다양한 모델을 단일 API 키로 통합하여 제공하므로, 나중에 모델을 교체하고 싶을 때도 코드의 모델 이름만 변경하면 됩니다.

👉 HolySheep AI 가입하고 무료 크레딧 받기