사이드 프로젝트나 작은 서비스를 운영하다 보면 항상 고민되는 것이 바로 '서버 비용'과 '관리의 번거로움'입니다. 특히 저처럼 외부 AI API(Google Gemini 등)를 호출하여 가공된 데이터를 제공하는 간단한 API 서버를 운영할 때, 24시간 켜져 있는 클라우드 서버(AWS, Oracle Cloud 등)는 다소 부담스럽게 느껴질 수 있습니다.
그래서 오늘은! 제가 운영하던 Python Flask 기반의 Gemini API 서버를 '서버리스 엣지 컴퓨팅'의 대표주자인 Cloudflare Workers로 이전 구축한 경험을 공유해드리고자 합니다.
왜 Cloudflare Workers인가? 기존 서버의 한계
제가 운영하던 Python 서버는 간단했습니다.
python
# 기존 Flask 서버 (개념)
from flask import Flask, request, jsonify
from flask_cors import CORS
import google.generativeai as genai
app = Flask(__name__)
CORS(app) # CORS 설정
# Gemini API 호출 로직
@app.route('/api/change', methods=['POST'])
def call_gemini():
# ... (요청 처리 및 Gemini API 호출)
return jsonify(result)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)
이 서버의 한계는 명확했습니다.
- 지리적 지연: 서버가 한국(또는 특정 국가)에 있어, 해외 사용자는 응답이 느립니다.
- 고정 비용: 트래픽이 없어도 서버를 켜두는 비용이 발생합니다.
- 관리 부담: 보안 패치, 서버 업데이트 등 주기적인 관리가 필요합니다.
Cloudflare Workers는 이 모든 문제를 해결해줍니다. 코드가 전 세계 수백 개 도시의 엣지(Edge) 네트워크에 분산 배포되어, 사용자와 가장 가까운 곳에서 실행되기 때문입니다.
구분 | 전통적인 클라우드 서버 | Cloudflare Workers |
속도 | 서버 위치에 따라 다름 | 전 세계 어디서나 매우 빠름 |
비용 | 고정비 또는 높은 사용량 요금 | 넉넉한 무료 플랜, 저렴한 사용량 요금 |
관리 | 직접 관리 필요 | 서버 관리 불필요 (서버리스) |
마이그레이션 전략: Python 로직을 JavaScript로!
Cloudflare Workers는 JavaScript/TypeScript를 네이티브 언어로 사용합니다. 따라서 Python 코드를 그대로 옮기는 것이 아니라, 핵심 로직을 JavaScript로 재구현해야 합니다. 걱정 마세요, 생각보다 간단합니다!
- Flask 라우팅 → itty-router 라이브러리
- google.genai 라이브러리 → fetch로 Gemini REST API 직접 호출
- API 키 관리 → wrangler secret (안전한 키 관리)
- CORS 설정 → 직접 헤더(Header) 설정
1단계: Cloudflare Workers 프로젝트 준비
먼저 개발 환경부터 설정합시다. Node.js가 설치되어 있어야 합니다.
- Wrangler CLI 설치 및 로그인: Cloudflare Workers를 위한 공식 커맨드라인 도구입니다.Generated bash
# Wrangler 설치 npm install -g wrangler # Cloudflare 계정 로그인 wrangler login
- 프로젝트 생성:Generated bash
wrangler init my-gemini-api cd my-gemini-api
- 라우터 라이브러리 설치: API 엔드포인트를 쉽게 관리하기 위해 itty-router를 설치합니다.Generated bash
npm install itty-router
- API 키 안전하게 등록: 절대 코드에 API 키를 넣지 마세요! 아래 명령어를 실행하고 터미널에 Gemini API 키를 붙여넣습니다.Generated bash
wrangler secret put GEMINI_API_KEY
2단계: 핵심 로직 JavaScript로 구현하기
src/index.js 파일을 열고, 기존 코드를 모두 지운 후 아래 코드로 교체합니다. 이 코드는 기존 Flask 서버의 /api/change 엔드포인트와 동일한 역할을 합니다.
src/index.js
Generated javascript
import { Router } from 'itty-router';
// 허용할 도메인 목록 (내 블로그 주소 등)
const ALLOWED_ORIGINS = [
"https://mydomain.com",
"http://localhost:3000" // 로컬 테스트용
];
const router = Router();
// CORS Preflight(OPTIONS) 요청을 처리하는 핸들러
const handleCors = request => {
const origin = request.headers.get("Origin");
if (ALLOWED_ORIGINS.includes(origin)) {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization", // 필요한 헤더 추가
},
});
}
return new Response(null, { headers: { "Allow": "GET, POST, OPTIONS" } });
};
router.options('*', handleCors);
// API 핵심 로직
router.post('/api/change', async (request, env) => {
// 요청 본문 파싱
const data = await request.json();
const { model: geminiModel, prompt } = data;
// Secret에서 API 키 가져오기
const GEMINI_API_KEY = env.GEMINI_API_KEY;
if (!GEMINI_API_KEY) {
return new Response('API key not configured', { status: 500 });
}
// Gemini REST API 직접 호출
const url = `https://generativelanguage.googleapis.com/v1beta/models/${geminiModel}:generateContent?key=${GEMINI_API_KEY}`;
const geminiResponse = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ contents: [{ parts: [{ text: prompt }] }] }) // Gemini API 요청 형식
});
if (!geminiResponse.ok) {
return new Response(`Gemini API Error: ${await geminiResponse.text()}`, { status: geminiResponse.status });
}
const geminiResult = await geminiResponse.json();
// 응답 헤더에 CORS 설정 추가
const origin = request.headers.get("Origin");
const headers = { 'Content-Type': 'application/json' };
if (ALLOWED_ORIGINS.includes(origin)) {
headers['Access-Control-Allow-Origin'] = origin;
}
// 최종 응답 생성 (Gemini 결과에서 텍스트만 추출)
const resultText = geminiResult?.candidates?.[0]?.content?.parts?.[0]?.text || '{}';
return new Response(JSON.stringify({ result: resultText }), { headers });
});
// 404 핸들러
router.all('*', () => new Response('404, Not Found.', { status: 404 }));
// Worker의 메인 진입점
export default {
fetch: router.handle
};
3단계: 배포 및 테스트
이제 모든 준비가 끝났습니다. 터미널에서 단 한 줄의 명령어로 전 세계에 코드를 배포할 수 있습니다.
Generated bash
wrangler deploy
배포가 성공하면 https://my-gemini-api.내-서브도메인.workers.dev 와 같은 주소를 얻게 됩니다. 이제 기존 클라이언트(티스토리 블로그 등)의 API 요청 주소를 이 새로운 Worker 주소로 변경하기만 하면 모든 이전 작업이 끝납니다!
결과 및 결론
저는 이 과정을 통해 다음과 같은 놀라운 결과를 얻었습니다.
- 응답 속도 개선: API 응답 시간이 체감될 정도로 빨라졌습니다.
- 비용 절감: 월 고정비가 사라지고, Cloudflare의 넉넉한 무료 플랜 덕분에 사실상 0원의 비용으로 운영 중입니다.
- 관리 해방: 서버가 다운될까 걱정하거나, 보안 패치를 신경 쓸 필요가 없어졌습니다.
물론 Python의 풍부한 생태계를 포기하고 JavaScript로 로직을 재작성하는 약간의 수고가 필요합니다. 하지만 저와 같이 외부 API를 중계하는 간단한 서버라면, Cloudflare Workers로 이전하는 것은 성능, 비용, 안정성 모든 면에서 '남는 장사'임이 틀림없습니다.
여러분의 사이드 프로젝트에도 '서버리스 엣지'라는 날개를 달아보시는 건 어떨까요?
'AI 코딩 개발 > AI 코딩' 카테고리의 다른 글
Windows에서 Python 3.7과 3.9 버전을 함께 사용하기 (0) | 2025.08.21 |
---|---|
개발자를 위한 Nginx 입문: 웹 서버, 리버스 프록시, 그리고 그 이상 🚀 (0) | 2025.08.08 |
구글 드라이브와 Cloudflare로 내 블로그에 '끊김 없는' 오디오 플레이어 만들기 (무료) (0) | 2025.08.02 |
Gemini TTS API에서 음성 속도 (0) | 2025.07.27 |
Next.js에서 다국어(i18n, Internationalization)를 설정하는 방법 (0) | 2025.07.17 |
댓글