AI API를 운영 환경에서 활용할 때 가장 중요한 문제 중 하나가 바로 사용량 통제입니다. 예기치 못한 비용 폭탄, 악의적인 남용, 또는 단순한 실수 하나로 수백만 원의 청구서가 날아올 수 있습니다. 이번 튜토리얼에서는 HolySheep AI 게이트웨이를 활용하여 견고한 API 사용량 할당량 적용 시스템을 구축하는 방법을 상세히 다룹니다.

HolySheep AI vs 공식 API vs 기타 릴레이 서비스 비교

특징 HolySheep AI 공식 API (OpenAI/Anthropic) 기타 릴레이 서비스
결제 방식 로컬 결제 (해외 신용카드 불필요) 해외 신용카드 필수 다양함 (카드/가상계좌)
API 키 관리 단일 키로 모든 모델 통합 모델별 별도 키 필요 서비스별 상이
기본 할당량 관리 대시보드 + 커스텀 구현 지원 기본 제공 (하드 리밋) 제한적
가격 (GPT-4.1) $8/MTok $15/MTok $10-14/MTok
가격 (Claude Sonnet) $4.5/MTok $6/MTok $4.5-5.5/MTok
가격 (Gemini 2.5 Flash) $2.50/MTok $3.50/MTok $2.50-3/MTok
가격 (DeepSeek V3.2) $0.42/MTok $0.55/MTok $0.42-0.50/MTok
무료 크레딧 가입 시 제공 $5 초기 크레딧 서비스별 상이
커스텀 할당량 로직 완벽 지원 (Rate Limiter + Quota) 제한적 (Tier 기반) 중간적

왜 커스텀 Quota 시스템을 구축해야 하는가?

저는 3년간 다양한 AI API 게이트웨이 운영 경험을 통해 하나의 교훈을 얻었습니다. 기본 제공 할당량 시스템만으로는 실제 프로덕션 환경의 요구사항을 충족할 수 없다는 것입니다.

공식 API의 기본 할당량은:

HolySheep AI는 이런 한계를 극복하면서도 지금 가입하면 누구나 저렴한 가격으로 고급 AI 모델을 사용할 수 있습니다. 이제 커스텀 Quota 시스템을 구축해 보겠습니다.

1. 할당량 적용 시스템 아키텍처


┌─────────────────────────────────────────────────────────────────┐
│                        요청 흐름도                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  클라이언트                                                      │
│     │                                                           │
│     ▼                                                           │
│  ┌──────────────┐                                               │
│  │  API Gateway │ ◄── HolySheep AI (base_url)                   │
│  │  (Quota Check)│     https://api.holysheep.ai/v1              │
│  └──────┬───────┘                                               │
│         │                                                        │
│    ┌────┴────┐                                                   │
│    │         │                                                    │
│    ▼         ▼                                                    │
│ ┌──────┐  ┌──────┐                                               │
│ │허용  │  │차단  │                                               │
│ │실행  │  │429응답│                                              │
│ └──────┘  └──────┘                                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. Node.js 기반 할당량 추적 시스템

가장 기본이 되는 구조부터 살펴보겠습니다. 각 사용자에게 할당량을 부여하고, API 호출 시마다 사용량을 차감하는 시스템을 구축합니다.

// quota-manager.js
// HolySheep AI API 사용량 할당량 관리 시스템

const axios = require('axios');

// HolySheep AI 설정
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';
const HOLYSHEEP_API_KEY = process.env.HOLYSHEEP_API_KEY;

// 메모리 기반 할당량 저장소 (프로덕션에서는 Redis 사용 권장)
const quotaStore = new Map();

// 사용자별 기본 할당량 설정 (토큰 단위)
const DEFAULT_QUOTAS = {
  free: 100000,        // 무료 사용자: 100K 토큰/일
  basic: 500000,       // 베이직: 500K 토큰/일
  pro: 2000000,        // 프로: 2M 토큰/일
  enterprise: 10000000 // 엔터프라이즈: 10M 토큰/일
};

// 할당량 클래스 정의
class QuotaManager {
  constructor() {
    this.resetDailyQuotas();
  }

  // 일일 할당량 초기화 (자정마다 실행)
  resetDailyQuotas() {
    const now = new Date();
    const msUntilMidnight = 
      (24 - now.getHours()) * 3600 * 1000 - 
      now.getMinutes() * 60 * 1000 - 
      now.getSeconds() * 1000;
    
    setTimeout(() => {
      this.quotaStore.forEach((data) => {
        data.dailyUsed = 0;
        data.lastReset = Date.now();
      });
      console.log('일일 할당량이 초기화되었습니다.');
      this.resetDailyQuotas(); // 다음 날 재귀 호출
    }, msUntilMidnight);
  }

  // 사용자 할당량 초기화
  initializeUser(userId, tier = 'free') {
    this.quotaStore.set(userId, {
      tier,
      dailyLimit: DEFAULT_QUOTAS[tier] || DEFAULT_QUOTAS.free,
      dailyUsed: 0,
      monthlyLimit: DEFAULT_QUOTAS[tier] * 30 || DEFAULT_QUOTAS.free * 30,
      monthlyUsed: 0,
      lastReset: Date.now(),
      blocked: false
    });
  }

  // 할당량 확인
  checkQuota(userId, tokens) {
    const userQuota = this.quotaStore.get(userId);
    
    if (!userQuota) {
      this.initializeUser(userId);
      return this.checkQuota(userId, tokens);
    }

    const remainingDaily = userQuota.dailyLimit - userQuota.dailyUsed;
    const remainingMonthly = userQuota.monthlyLimit - userQuota.monthlyUsed;
    
    return {
      allowed: remainingDaily >= tokens && remainingMonthly >= tokens && !userQuota.blocked,
      remainingDaily,
      remainingMonthly,
      blocked: userQuota.blocked,
      retryAfter: userQuota.blocked ? 3600 : null
    };
  }

  // 토큰 사용량 차감
  deductQuota(userId, tokens) {
    const userQuota = this.quotaStore.get(userId);
    if (userQuota) {
      userQuota.dailyUsed += tokens;
      userQuota.monthlyUsed += tokens;
    }
  }

  // HolySheep AI API 호출 전 할당량 검증
  async validateAndCall(userId, model, messages) {
    // 먼저 토큰 예상치 계산 (대략적인估算)
    const estimatedTokens = this.estimateTokens(messages);
    
    // 할당량 확인
    const quotaCheck = this.checkQuota(userId, estimatedTokens);
    
    if (!quotaCheck.allowed) {
      return {
        error: true,
        code: 'QUOTA_EXCEEDED',
        message: 일일 할당량(${quotaCheck.remainingDaily} 토큰)이 부족합니다.,
        retryAfter: quotaCheck.retryAfter
      };
    }

    try {
      // HolySheep AI API 호출
      const response = await axios.post(
        ${HOLYSHEEP_BASE_URL}/chat/completions,
        {
          model,
          messages
        },
        {
          headers: {
            'Authorization': Bearer ${HOLYSHEEP_API_KEY},
            'Content-Type': 'application/json'
          }
        }
      );

      // 실제 사용량으로 차감 (응답의 usage 필드 활용)
      const actualTokens = response.data.usage?.total_tokens || estimatedTokens;
      this.deductQuota(userId, actualTokens);

      return {
        error: false,
        data: response.data,
        quotaInfo: {
          used: actualTokens,
          remainingDaily: this.quotaStore.get(userId).dailyLimit - 
                          this.quotaStore.get(userId).dailyUsed
        }
      };
    } catch (error) {
      console.error('HolySheep AI API 오류:', error.response?.data || error.message);
      throw error;
    }
  }

  // 토큰 수 추정 (대략적)
  estimateTokens(messages) {
    return messages.reduce((sum, msg) => sum + Math.ceil(msg.content.length / 4), 0);
  }
}

module.exports = new QuotaManager();

3. Express.js로 구현하는 API 라우터

이제 위의 할당량 관리자를 실제 API 라우터에 통합하겠습니다. HolySheep AI를 백엔드로 사용하면서 사용자별 할당량을 자동으로 검증합니다.

// api/routes/ai.js
// HolySheep AI API 라우터 + 할당량 검증

const express = require('express');
const router = express.Router();
const quotaManager = require('../quota-manager');

// HolySheep AI 지원 모델 목록
const SUPPORTED_MODELS = {
  'gpt-4.1': { provider: 'openai', pricePerMToken: 8 },
  'gpt-4.1-mini': { provider: 'openai', pricePerMToken: 2 },
  'claude-sonnet-4': { provider: 'anthropic', pricePerMToken: 4.5 },
  'claude-opus-4': { provider: 'anthropic', pricePerMToken: 15 },
  'gemini-2.5-flash': { provider: 'google', pricePerMToken: 2.50 },
  'deepseek-v3.2': { provider: 'deepseek', pricePerMToken: 0.42 }
};

// POST /api/ai/chat - HolySheep AI 채팅 완료
router.post('/chat', async (req, res) => {
  const { userId } = req.headers;
  const { model = 'gpt-4.1', messages, temperature = 0.7, max_tokens = 4096 } = req.body;

  // 입력 검증
  if (!userId) {
    return res.status(401).json({ 
      error: '사용자 인증이 필요합니다.',
      code: 'MISSING_USER_ID'
    });
  }

  if (!messages || !Array.isArray(messages) || messages.length === 0) {
    return res.status(400).json({
      error: 'messages 배열이 필요합니다.',
      code: 'INVALID_MESSAGES'
    });
  }

  if (!SUPPORTED_MODELS[model]) {
    return res.status(400).json({
      error: 지원되지 않는 모델입니다. 지원 모델: ${Object.keys(SUPPORTED_MODELS).join(', ')},
      code: 'UNSUPPORTED_MODEL'
    });
  }

  try {
    // 할당량 검증 및 API 호출
    const result = await quotaManager.validateAndCall(userId, model, messages);

    if (result.error) {
      return res.status(429).json({
        error: result.message,
        code: result.code,
        retryAfter: result.retryAfter
      });
    }

    // 비용 계산 (디버깅/로깅용)
    const modelInfo = SUPPORTED_MODELS[model];
    const costUSD = (result.quotaInfo.used / 1000000) * modelInfo.pricePerMToken;
    
    console.log([${userId}] ${model} 호출: ${result.quotaInfo.used} 토큰 사용, $${costUSD.toFixed(4)});

    // 성공 응답
    res.json({
      success: true,
      model: result.data.model,
      content: result.data.choices[0]?.message?.content,
      usage: result.data.usage,
      quota: {
        used: result.quotaInfo.used,
        remaining: result.quotaInfo.remainingDaily
      }
    });

  } catch (error) {
    console.error('API 호출 오류:', error.response?.data || error.message);
    
    res.status(error.response?.status || 500).json({
      error: 'AI API 호출 중 오류가 발생했습니다.',
      details: error.response?.data?.error?.message || error.message
    });
  }
});

// GET /api/ai/quota - 현재 할당량 조회
router.get('/quota', (req, res) => {
  const { userId } = req.headers;

  if (!userId) {
    return res.status(401).json({ error: '사용자 인증이 필요합니다.' });
  }

  const quota = quotaManager.quotaStore.get(userId);

  if (!quota) {
    return res.json({
      userId,
      tier: 'free',
      dailyLimit: 100000,
      dailyUsed: 0,
      remaining: 100000
    });
  }

  res.json({
    userId,
    tier: quota.tier,
    dailyLimit: quota.dailyLimit,
    dailyUsed: quota.dailyUsed,
    monthlyLimit: quota.monthlyLimit,
    monthlyUsed: quota.monthlyUsed,
    remaining: quota.dailyLimit - quota.dailyUsed,
    blocked: quota.blocked
  });
});

// PUT /api/ai/quota/tier - 사용자 티어 변경 (관리자용)
router.put('/quota/tier', (req, res) => {
  const { userId, tier } = req.body;

  if (!['free', 'basic', 'pro', 'enterprise'].includes(tier)) {
    return res.status(400).json({ error: '유효하지 않은 티어입니다.' });
  }

  quotaManager.initializeUser(userId, tier);
  
  res.json({
    success: true,
    message: ${userId} 사용자가 ${tier}으로 변경되었습니다.
  });
});

module.exports = router;

4. Redis를 활용한 분산 환경 할당량 관리

단일 서버 환경에서는 위의 메모리 기반 저장소로 충분하지만, 마이크로서비스架构나 다중 서버 환경에서는 Redis를 활용해야 합니다. HolySheep AI의 고가용성을 최대한 활용하기 위한 분산 Quota 시스템을 구현합니다.

// distributed-quota.js
// Redis 기반 분산 환경 할당량 관리 시스템

const Redis = require('ioredis');
const axios = require('axios');

// HolySheep AI 설정
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';
const HOLYSHEEP_API_KEY = process.env.HOLYSHEEP_API_KEY;

// Redis 클라이언트 설정
const redis = new Redis({
  host: process.env.REDIS_HOST || 'localhost',
  port: process.env.REDIS_PORT || 6379,
  password: process.env.REDIS_PASSWORD,
  retryDelayOnFailover: 100,
  maxRetriesPerRequest: 3
});

class DistributedQuotaManager {
  constructor() {
    this.LUA_SCRIPT_QUOTA_CHECK = `
      local user_key = KEYS[1]
      local requested = tonumber(ARGV[1])
      local daily_limit = tonumber(ARGV[2])
      local monthly_limit = tonumber(ARGV[3])
      
      -- 현재 사용량 조회
      local daily_used = tonumber(redis.call('GET', user_key .. ':daily') or '0')
      local monthly_used = tonumber(redis.call('GET', user_key .. ':monthly') or '0')
      local blocked = redis.call('GET', user_key .. ':blocked')
      
      -- 차단된 사용자 체크
      if blocked == '1' then
        return {0, daily_used, monthly_used, daily_limit - daily_used, 'BLOCKED'}
      end
      
      -- 일일/월간 할당량 체크
      if daily_used + requested > daily_limit then
        return {0, daily_used, monthly_used, daily_limit - daily_used, 'DAILY_EXCEEDED'}
      end
      
      if monthly_used + requested > monthly_limit then
        return {0, daily_used, monthly_used, monthly_limit - monthly_used, 'MONTHLY_EXCEEDED'}
      end
      
      -- 할당량 차감 (Atomic 연산)
      redis.call('INCRBY', user_key .. ':daily', requested)
      redis.call('INCRBY', user_key .. ':monthly', requested)
      redis.call('EXPIRE', user_key .. ':daily', 86400)
      redis.call('EXPIRE', user_key .. ':monthly', 2592000)
      
      return {1, daily_used + requested, monthly_used + requested, daily_limit - daily_used - requested, 'OK'}
    `;
  }

  // 할당량 체크 및 차감 (Atomic Lua 스크립트 실행)
  async checkAndDeduct(userId, tokens) {
    const userKey = quota:${userId};
    const limits = await this.getUserLimits(userId);
    
    const result = await redis.eval(
      this.LUA_SCRIPT_QUOTA_CHECK,
      1,
      userKey,
      tokens,
      limits.dailyLimit,
      limits.monthlyLimit
    );

    return {
      allowed: result[0] === 1,
      dailyUsed: result[1],
      monthlyUsed: result[2],
      remaining: result[3],
      reason: result[4]
    };
  }

  // 사용자 할당량 한도 조회 (또는 기본값 반환)
  async getUserLimits(userId) {
    const tier = await redis.get(user:${userId}:tier) || 'free';
    
    const tierLimits = {
      free: { daily: 100000, monthly: 3000000 },
      basic: { daily: 500000, monthly: 15000000 },
      pro: { daily: 2000000, monthly: 60000000 },
      enterprise: { daily: 10000000, monthly: 300000000 }
    };

    return {
      dailyLimit: tierLimits[tier]?.daily || 100000,
      monthlyLimit: tierLimits[tier]?.monthly || 3000000,
      tier
    };
  }

  // HolySheep AI API 호출 통합
  async callWithQuota(userId, model, messages, options = {}) {
    const estimatedTokens = this.estimateTokens(messages);
    
    // 1단계: 할당량 검증
    const quotaResult = await this.checkAndDeduct(userId, estimatedTokens);
    
    if (!quotaResult.allowed) {
      const errorMessages = {
        'BLOCKED': '계정이 차단되었습니다.',
        'DAILY_EXCEEDED': 일일 할당량이 초과되었습니다. (잔여: ${quotaResult.remaining} 토큰),
        'MONTHLY_EXCEEDED': 월간 할당량이 초과되었습니다.
      };
      
      throw new QuotaExceededError(
        errorMessages[quotaResult.reason] || '할당량이 부족합니다.',
        quotaResult
      );
    }

    try {
      // 2단계: HolySheep AI API 호출
      const response = await axios.post(
        ${HOLYSHEEP_BASE_URL}/chat/completions,
        {
          model,
          messages,
          temperature: options.temperature || 0.7,
          max_tokens: options.max_tokens || 4096
        },
        {
          headers: {
            'Authorization': Bearer ${HOLYSHEEP_API_KEY},
            'Content-Type': 'application/json'
          }
        }
      );

      // 3단계: 실제 사용량 보정 (초과 책정)
      const actualTokens = response.data.usage?.total_tokens || estimatedTokens;
      const difference = actualTokens - estimatedTokens;
      
      if (difference > 0) {
        await redis.incrby(quota:${userId}:daily, difference);
        await redis.incrby(quota:${userId}:monthly, difference);
      }

      return {
        content: response.data.choices[0]?.message?.content,
        usage: response.data.usage,
        quota: {
          dailyUsed: quotaResult.dailyUsed + difference,
          dailyRemaining: quotaResult.remaining - difference,
          estimatedCost: this.calculateCost(model, actualTokens)
        }
      };

    } catch (error) {
      // API 실패 시 차감한 토큰 복구
      await redis.decrby(quota:${userId}:daily, estimatedTokens);
      await redis.decrby(quota:${userId}:monthly, estimatedTokens);
      throw error;
    }
  }

  estimateTokens(messages) {
    return messages.reduce((sum, msg) => 
      sum + Math.ceil((msg.content || '').length / 4), 0
    );
  }

  calculateCost(model, tokens) {
    const prices = {
      'gpt-4.1': 8,
      'gpt-4.1-mini': 2,
      'claude-sonnet-4': 4.5,
      'claude-opus-4': 15,
      'gemini-2.5-flash': 2.50,
      'deepseek-v3.2': 0.42
    };
    return ((tokens / 1000000) * (prices[model] || 8)).toFixed(4);
  }

  // 사용량 통계 조회
  async getUsageStats(userId) {
    const dailyUsed = await redis.get(quota:${userId}:daily) || 0;
    const monthlyUsed = await redis.get(quota:${userId}:monthly) || 0;
    const limits = await this.getUserLimits(userId);

    return {
      daily: { used: parseInt(dailyUsed), limit: limits.dailyLimit },
      monthly: { used: parseInt(monthlyUsed), limit: limits.monthlyLimit },
      percentUsed: ((parseInt(dailyUsed) / limits.dailyLimit) * 100).toFixed(2)
    };
  }
}

class QuotaExceededError extends Error {
  constructor(message, quotaInfo) {
    super(message);
    this.name = 'QuotaExceededError';
    this.quotaInfo = quotaInfo;
  }
}

module.exports = new DistributedQuotaManager();

5. 할당량 초과 시 자동 알림 시스템

저는 실제 운영에서 사용량이 80%에 도달하면 경고, 95%에 도달하면 슬랙 알림을 보내는 체계를 운영합니다. 이로 인해突발적 할당량 소진으로 서비스가 중단되는 일을 방지했습니다.

// quota-monitor.js
// 할당량 모니터링 및 자동 알림 시스템

const axios = require('axios');
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';

class QuotaMonitor {
  constructor() {
    this.alertThresholds = {
      warning: 0.8,    // 80% 사용 시 경고
      critical: 0.95,  // 95% 사용 시 위기
      exceeded: 1.0    // 100% 초과 시 차단
    };
    this.notifiedUsers = new Set(); // 중복 알림 방지
  }

  // 사용량 체크 및 알림
  async checkAndAlert(userId, quotaInfo, options = {}) {
    const usagePercent = quotaInfo.used / quotaInfo.limit;
    
    const alerts = [];

    // 임계값 체크
    if (usagePercent >= this.alertThresholds.exceeded) {
      alerts.push({
        level: 'EXCEEDED',
        message: 🚫 ${userId} 사용자가 할당량을 100% 초과했습니다!,
        action: 'BLOCK_USER'
      });
      
      if (options.blockUser) {
        await this.blockUser(userId);
      }
    }
    
    if (usagePercent >= this.alertThresholds.critical) {
      alerts.push({
        level: 'CRITICAL',
        message: ⚠️ ${userId} 사용자가 할당량의 95%를 사용했습니다!,
        action: 'NOTIFY_ADMIN'
      });
    }
    
    if (usagePercent >= this.alertThresholds.warning) {
      alerts.push({
        level: 'WARNING',
        message: 📊 ${userId} 사용자가 할당량의 80%를 사용했습니다.,
        action: 'NOTIFY_USER'
      });
    }

    // 알림 전송 (중복 방지)
    for (const alert of alerts) {
      const alertKey = ${userId}:${alert.level};
      
      if (!this.notifiedUsers.has(alertKey)) {
        await this.sendAlert(alert, userId, quotaInfo, options);
        this.notifiedUsers.add(alertKey);
        
        // 1시간 후 알림 키 제거 (재알림 가능하도록)
        setTimeout(() => this.notifiedUsers.delete(alertKey), 3600000);
      }
    }

    return alerts;
  }

  // 알림 전송 (여러 채널 지원)
  async sendAlert(alert, userId, quotaInfo, options) {
    const { slackWebhook, emailConfig, telegramBot } = options;

    // 슬랙 알림
    if (slackWebhook && alert.level !== 'WARNING') {
      await this.sendSlackAlert(slackWebhook, alert, userId, quotaInfo);
    }

    // 이메일 알림
    if (emailConfig && alert.level === 'CRITICAL') {
      await this.sendEmailAlert(emailConfig, alert, userId, quotaInfo);
    }

    // 텔레그램 알림
    if (telegramBot && alert.level === 'EXCEEDED') {
      await this.sendTelegramAlert(telegramBot, alert, userId, quotaInfo);
    }

    console.log([${alert.level}] ${alert.message});
  }

  async sendSlackAlert(webhook, alert, userId, quotaInfo) {
    try {
      await axios.post(webhook, {
        blocks: [
          {
            type: 'header',
            text: { type: 'plain_text', text: 🚨 AI API 할당량 알림 - ${alert.level} }
          },
          {
            type: 'section',
            fields: [
              { type: 'mrkdwn', text: *사용자:*\n${userId} },
              { type: 'mrkdwn', text: *사용량:*\n${quotaInfo.used.toLocaleString()} / ${quotaInfo.limit.toLocaleString()} },
              { type: 'mrkdwn', text: *사용률:*\n${((quotaInfo.used / quotaInfo.limit) * 100).toFixed(1)}% },
              { type: 'mrkdwn', text: *권장 조치:*\n${alert.action} }
            ]
          }
        ]
      });
    } catch (error) {
      console.error('슬랙 알림 전송 실패:', error.message);
    }
  }

  async sendTelegramAlert(botConfig, alert, userId, quotaInfo) {
    try {
      const message = 🚨 *HolySheep AI 할당량 알림*\n\n +
        사용자: ${userId}\n +
        사용량: ${quotaInfo.used.toLocaleString()} 토큰\n +
        한도: ${quotaInfo.limit.toLocaleString()} 토큰\n +
        사용률: ${((quotaInfo.used / quotaInfo.limit) * 100).toFixed(1)}%\n\n +
        조치: ${alert.action};

      await axios.post(https://api.telegram.org/bot${botConfig.token}/sendMessage, {
        chat_id: botConfig.chatId,
        text: message,
        parse_mode: 'Markdown'
      });
    } catch (error) {
      console.error('텔레그램 알림 전송 실패:', error.message);
    }
  }

  async blockUser(userId) {
    // Redis에 사용자 차단 상태 설정
    const redis = require('ioredis');
    const redisClient = new Redis(process.env.REDIS_URL);
    
    await redisClient.set(user:${userId}:blocked, '1', 'EX', 86400);
    console.log(사용자 ${userId}가 자동 차단되었습니다.);
  }
}

module.exports = new QuotaMonitor();

실전 활용 예시: Express 서버 통합

// server.js
// 전체 시스템 통합 - HolySheep AI API + 할당량 관리

const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const quotaManager = require('./distributed-quota');
const quotaMonitor = require('./quota-monitor');

const app = express();
app.use(helmet());
app.use(express.json());

// 기본 Rate Limiter (DDoS 방지)
const globalLimiter = rateLimit({
  windowMs: 1 * 60 * 1000, // 1분
  max: 100,
  message: { error: '요청이 너무 많습니다. 잠시 후 다시 시도하세요.' }
});

app.use(globalLimiter);

// HolySheep AI 채팅 API
app.post('/api/v1/chat', async (req, res) => {
  const userId = req.headers['x-user-id'];
  const { model, messages, temperature, max_tokens } = req.body;

  if (!userId) {
    return res.status(401).json({ error: 'x-user-id 헤더가 필요합니다.' });
  }

  try {
    const result = await quotaManager.callWithQuota(userId, model, messages, {
      temperature,
      max_tokens
    });

    // 사용량 체크 및 알림 (80%, 95%, 100% 임계값)
    const stats = await quotaManager.getUsageStats(userId);
    await quotaMonitor.checkAndAlert(userId, stats.daily, {
      slackWebhook: process.env.SLACK_WEBHOOK,
      telegramBot: {
        token: process.env.TELEGRAM_BOT_TOKEN,
        chatId: process.env.TELEGRAM_CHAT_ID
      },
      blockUser: true
    });

    res.json({
      success: true,
      content: result.content,
      usage: result.usage,
      quota: result.quota,
      stats
    });

  } catch (error) {
    if (error.name === 'QuotaExceededError') {
      return res.status(429).json({
        error: error.message,
        code: 'QUOTA_EXCEEDED',
        quota: error.quotaInfo,
        retryAfter: 86400
      });
    }

    console.error('API 오류:', error);
    res.status(500).json({ error: '서버 오류가 발생했습니다.' });
  }
});

// 할당량 조회 API
app.get('/api/v1/quota', async (req, res) => {
  const userId = req.headers['x-user-id'];
  
  if (!userId) {
    return res.status(401).json({ error: 'x-user-id 헤더가 필요합니다.' });
  }

  const stats = await quotaManager.getUsageStats(userId);
  res.json(stats);
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(🚀 HolySheep AI API 서버 실행 중 (포트: ${PORT}));
  console.log(📡 base_url: https://api.holysheep.ai/v1);
});

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

오류 1: 401 Unauthorized - API 키 인증 실패

{
  "error": {
    "message": "Incorrect API key provided",
    "type": "invalid_request_error",
    "code": "invalid_api_key"
  }
}

원인: HolySheep AI API 키가 유효하지 않거나 환경 변수 설정이 누락되었습니다.

해결:

# .env 파일 확인
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY  # 실제 키로 교체

서버 재시작 후 테스트

Node.js에서 키 확인

console.log('API Key:', process.env.HOLYSHEEP_API_KEY ? '설정됨' : '누락'); // 키 발급: https://www.holysheep.ai/register 에서 가입 후 Dashboard에서 확인

오류 2: 429 Too Many Requests - Rate Limit 초과

{
  "error": "일일 할당량이 초과되었습니다.",
  "code": "QUOTA_EXCEEDED",
  "retryAfter": 3600,
  "quota": {
    "dailyUsed": 100000,
    "dailyLimit": 100000,
    "remaining": 0
  }
}

원인: 일일 또는 월간 할당량 한도에 도달했습니다.

해결:

관련 리소스

관련 문서