API 키 유출 없이 모든 웹사이트에 AI 채팅 위젯 추가하기
요약
웹사이트에 AI 채팅 위젯을 추가할 때 API 키 유출을 방지하는 안전한 구현 방법을 안내합니다. 프론트엔드에 키를 직접 노출하지 않도록 BYOK 모드와 프록시 모드라는 두 가지 설계 방식을 제안합니다.
핵심 포인트
- 브라우저에 노출된 API 키는 결제 자격 증명이 유출될 위험이 있음
- BYOK 모드는 방문자가 자신의 키를 직접 사용하여 개인용 도구에 적합함
- 프록시 모드는 서버에서 키를 주입하여 공개 웹사이트의 보안을 유지함
- 11KB의 가벼운 바닐라 JS 위젯으로 OpenAI 호환 엔드포인트와 통신 가능
웹사이트에 AI 채팅 박스를 추가하는 것은 5분이면 끝나는 작업입니다. 하지만 대부분의 5분짜리 튜토리얼은 가장 중요한 한 가지를 잘못하고 있습니다. 바로 프론트엔드(front-end)에 API 키를 넣는 것입니다. 이 가이드에서는 사용자가 그런 실수를 하지 못하도록 차단하는, 의존성 없는 아주 작은 위젯을 사용하며, 페이지가 본인 소유인지 혹은 공개된 페이지인지에 따라 키를 처리하는 두 가지 올바른 방법을 보여줍니다.
이 위젯은 빌드 단계나 프레임워크가 필요 없는 약 11 KB 크기의 바닐라 JS (vanilla JS)로 구성되어 있으며, 모든 OpenAI 호환 (OpenAI-compatible) Chat Completions 엔드포인트와 통신합니다. 기본값으로 daoxe를 사용하겠지만 (아래 공개 사항 참조), 한 줄만 수정하면 어디로든 연결할 수 있습니다.
공개 사항 (Disclosure): 저는 이 위젯의 기본 엔드포인트인 daoxe에서 일하고 있습니다. 이 글의 내용은 여기에 종속되지 않습니다. 한 줄만 바꾸면 어떤 호환 엔드포인트와도 통신할 수 있습니다.
불편한 진실: 브라우저에 있는 모든 것은 공개되어 있습니다
LLM API 키는 **결제 자격 증명 (billing credential)**입니다. JS 번들(JS bundle)에 포함된 키를 포함하여 브라우저로 전송하는 모든 것은 모든 방문자가 읽을 수 있습니다. 따라서 이 위젯은 설정에 의도적으로 apiKey 옵션을 포함하지 않습니다. 대신 다음 두 가지 모드 중 하나를 선택해야 합니다:
| 모드 | 키를 보유하는 주체 | 용도 | 트레이드오프 (Trade-off) |
|---|---|---|---|
byok (기본값) | 방문자가 자신의 키를 붙여넣음; 방문자의 브라우저에만 유지됨 | 본인만 사용하는 개인용/로컬 페이지; 대시보드; 내부 도구 | 엔드포인트가 브라우저 CORS를 허용해야 함 |
proxy (공개 사이트 권장) | 사용자의 서버가 키를 주입; 브라우저는 키를 절대 볼 수 없음 | 모든 공개 웹사이트 | 작은 프록시 (proxy)를 실행해야 함 (예시 포함) |
이 단 하나의 설계 선택이 핵심입니다. 만약 위젯이 공개 페이지의 소스에 사용자의 키를 직접 넣을 수 있게 허용한다면, 그것은 사용자의 자격 증명을 세상에 배포하는 것과 같습니다.
A) BYOK 모드 — 본인 소유의 페이지를 위한 모드
개인용 대시보드나 내부 도구의 경우, 방문자가 자신의 키를 제공하며, 이 키는 방문자의 브라우저 localStorage에만 저장되어 엔드포인트로 직접 전송됩니다:
<script src="daoxe-widget.js"></script>
<script>
DaoxeChat.init({
...
이는 엔드포인트가 허용적인 CORS 헤더를 반환할 때만 작동합니다 (많은 게이트웨이가 /chat/completions에서 CORS를 활성화하지 않으므로, 이것이 프록시 모드(proxy mode)가 필요한 이유입니다). 만약 "network/CORS" 에러가 발생한다면 프록시 모드로 전환하세요.
B) 프록시 모드 (Proxy mode) — 공개 사이트용
공개적인 사이트의 경우, 브라우저는 사용자의 라우트와 통신하며, 서버가 키를 추가합니다:
<script src="daoxe-widget.js"></script>
<script>
DaoxeChat.init({
...
즉시 실행 가능한 프록시 예제는 examples/에 포함되어 있습니다:
- Cloudflare Worker — 몇 분 안에 배포 가능하며, 키는 Worker secret으로 저장됩니다.
- Node/Express — 환경 변수에서
DAOXE_API_KEY를 읽어와 그대로 스트리밍합니다.
두 방식 모두 키를 서버 측에 유지하며, 프록시가 전달할 모델을 선택적으로 허용 목록(allow-list)에 추가할 수 있습니다. 프로덕션 환경에서는 Access-Control-Allow-Origin을 귀하의 도메인으로 제한하십시오.
방문자 ──▶ /api/chat (키를 보유한 귀하의 프록시) ──▶ https://daoxe.com/v1/chat/completions
◀── 스트리밍된 토큰 ◀───────────────────
알아두어야 할 보안 세부 사항
채팅 위젯은 신뢰할 수 없는 모델 출력물을 귀하의 페이지에 렌더링하므로, 키 외에도 두 가지 사항이 중요합니다:
- 위젯은 렌더링 전 모든 모델 출력물을 이스케이프(escape) 처리합니다 — 모델의 원문 텍스트를
innerHTML로 직접 삽입하지 않습니다. 작은 마크다운 스타일의 렌더러가 이미 이스케이프된 텍스트 내의 코드/굵게/줄바꿈을 처리하며, 모델이 반환한 HTML을 실행하지 않습니다. (직접 위젯을 구축한다면 반드시 이렇게 하십시오. 모델 출력은 신뢰할 수 없는 입력값입니다.) - 프록시를 잠그십시오. CORS를 귀하의 도메인으로 제한하고, 선택적으로 모델 허용 목록을 설정하여 프록시 URL이 유출되더라도 귀하의 비용으로 임의의 고비용 모델을 실행할 수 없도록 하십시오.
설정 주요 사항
DaoxeChat.init({ ... })는 일반적인 설정값들인 title, subtitle, greeting, accent, position, startOpen, systemPrompt, 그리고 (BYOK 전용) temperature / maxTokens를 지원합니다. 특히 언급할 만한 두 가지는 다음과 같습니다:
model은 필수 (required) 항목입니다 — 해당 키/계정에서 사용 가능한 정확한 ID여야 합니다. 블로그 포스트에 있는 리스트를 하드코딩하지 마세요. 권위 있는 리스트는GET {baseUrl}/models를 통해 확인할 수 있습니다.storageKey는 BYOK를 위한 localStorage 키를 제어합니다. 방문자의 키를 영구적으로 저장하지 않으려면null로 설정하세요.
이 위젯은 설계 단계부터 최소한의 기능만을 갖추고 있습니다: 파일 업로드, 이미지 입력, 도구 호출 (tool calls), 페이지 간 지속성 (cross-page persistence) 기능이 없습니다. 스트리밍 (Streaming)은 표준 OpenAI SSE 형식을 사용하며, 엔드포인트가 스트리밍을 지원하지 않는 경우 단일 JSON 읽기 방식으로 대체됩니다.
OpenAI 호환 엔드포인트가 여기서 도움이 되는 이유
위젯이 표준 Chat Completions 형태를 사용하기 때문에, 모델 ID만 변경하면 동일한 임베드 (embed)로 GPT, Claude, Gemini 또는 DeepSeek를 서비스할 수 있습니다. 즉, 하나의 키와 하나의 청구서로 운영할 수 있으며, 신뢰하고 검증된 어떤 제공업체로든 baseUrl을 다시 지정할 수 있습니다. daoxe가 기본값입니다 (https://daoxe.com/v1, 여러 모델에 걸친 하나의 키, Anthropic Messages도 네이티브 지원). 단, daoxe는 중국 본토에서는 사용할 수 없습니다 — 하지만 위젯은 특정 서비스에 종속되지 않습니다. baseUrl과 "Powered by" 링크만 한 줄 수정하면 됩니다.
요약 (TL;DR)
- 하나의
<script>태그, 약 11 KB, 빌드 과정 불필요, 모든 OpenAI 호환 엔드포인트 지원. - 절대로 프론트엔드 코드에 키를 넣지 마세요. BYOK (사용자의 페이지) 또는 프록시 (proxy) (공개 사이트)를 사용하세요.
- 프록시 예시 포함 (Cloudflare Worker, Node/Express); 운영 환경에서는 CORS를 귀하의 도메인으로 제한하세요.
- 모델 출력값을 이스케이프 (Escape) 하세요;
/v1/models로부터 정확한modelID를 요구하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기