AI 서비스 운영에서 버전 관리는 선택이 아닌 필수입니다. 모델 업데이트, 프롬프트 변경, 또는 새로운 기능 배포 후 예기치 않은 문제가 발생하면 빠른 롤백이 수익과 신뢰를 지키는 마지막 방어선입니다. 이번 튜토리얼에서는 HolySheep AI를 활용한 실전 롤백 아키텍처를 단계별로 설계하겠습니다.

실제 사용 사례: 롤백이 필요한 순간들

사례 1: 이커머스 AI 고객 서비스 급증

저는 지난 블랙프라이드 기간 중 AI 고객 서비스 챗봇을 GPT-4에서 새로운 모델로 마이그레이션한 경험이 있습니다. 트래픽이 평소의 15배로 급증하는 상황에서 새 모델이 예상보다 높은 토큰 소비를 보여 예산 초과 위험에 처했습니다. 롤백 없이는 수백만 원의 추가 비용이 발생할 뻔했습니다.

사례 2: 기업 RAG 시스템 출시

엔터프라이즈 고객을 위한 RAG 기반 지식 검색 시스템을 배포한 날, 특정 산업 용어에서 검색 정확도가 40% 하락하는 문제가 발생했습니다. 클라이언트 니즈에 맞춰 프롬프트를 최적화한 직후였고, 즉시 이전 버전으로 복원해야 했습니다.

사례 3: 개인 개발자 프로젝트

사이드 프로젝트로 운영하는 AI 번역 서비스에서 모델 업데이트 후 반응 시간(latency)이 3초에서 8초로 증가하는 문제가 생겼습니다. 작은 프로젝트였지만 사용자 이탈이 급격히 발생했고, 롤백 없이는 서비스 종료로 이어질 뻔했습니다.

버전 롤백 아키텍처 핵심 설계

효과적인 롤백 시스템은 다음 네 가지 요소를 기반으로 설계해야 합니다:

HolySheep AI 기반 롤백 구현

HolySheep AI의 단일 API 키로 여러 모델을 연결하는 특성을 활용하면, 롤백 시 별도 설정 변경 없이 다른 모델로 즉시 전환할 수 있습니다. 아래는 Python 기반의 완전한 롤백 시스템 구현입니다.

1단계: 롤백 매니저 클래스 구현

import os
import time
import json
import logging
from datetime import datetime
from enum import Enum
from typing import Optional, Dict, Any, List
from dataclasses import dataclass, field
from holySheep_client import HolySheepAIClient

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ModelVersion(Enum):
    """사용 가능한 모델 버전 정의"""
    PRIMARY = "gpt-4.1"
    FALLBACK = "claude-sonnet-4-20250514"
    EMERGENCY = "gemini-2.5-flash"
    ECONOMY = "deepseek-v3.2"

@dataclass
class VersionConfig:
    """버전별 구성 정보"""
    name: str
    model: str
    max_tokens: int = 4096
    temperature: float = 0.7
    max_cost_per_request: float = 0.50
    max_latency_ms: int = 3000
    weight: int = 100

@dataclass
class HealthMetrics:
    """헬스 메트릭 데이터"""
    timestamp: datetime
    latency_ms: float
    error_rate: float
    cost_per_request: float
    success_count: int = 0
    failure_count: int = 0

class RollbackManager:
    """
    AI 서비스 버전 롤백 관리자
    
    주요 기능:
    - 모델별 건강 상태 모니터링
    - 자동/수동 롤백 트리거
    - 비용 및 지연 시간 기반 알림
    - 버전 히스토리 추적
    """
    
    def __init__(self, api_key: str):
        self.client = HolySheepAIClient(api_key=api_key)
        self.base_url = "https://api.holysheep.ai/v1"
        
        # 버전 구성 정의
        self.versions: Dict[str, VersionConfig] = {
            "production": VersionConfig(
                name="production",
                model=ModelVersion.PRIMARY.value,
                max_tokens=4096,
                temperature=0.7,
                max_cost_per_request=0.50,
                max_latency_ms=3000,
                weight=100
            ),
            "fallback": VersionConfig(
                name="fallback",
                model=ModelVersion.FALLBACK.value,
                max_tokens=4096,
                temperature=0.7,
                max_cost_per_request=0.75,
                max_latency_ms=4000,
                weight=0
            ),
            "emergency": VersionConfig(
                name="emergency",
                model=ModelVersion.EMERGENCY.value,
                max_tokens=8192,
                temperature=0.5,
                max_cost_per_request=0.25,
                max_latency_ms=2000,
                weight=0
            ),
            "economy": VersionConfig(
                name="economy",
                model=ModelVersion.ECONOMY.value,
                max_tokens=4096,
                temperature=0.6,
                max_cost_per_request=0.15,
                max_latency_ms=3500,
                weight=0
            )
        }
        
        self.current_version = "production"
        self.health_history: List[HealthMetrics] = []
        self.rollback_history: List[Dict[str, Any]] = []
        
    def execute_rollback(self, target_version: str, reason: str) -> bool:
        """指定バージョンへのロールバック実行"""
        if target_version not in self.versions:
            logger.error(f"알 수 없는 버전: {target_version}")
            return False
        
        old_version = self.current_version
        self.current_version = target_version
        
        # 버전 가중치 조정
        self.versions[old_version].weight = 0
        self.versions[target_version].weight = 100
        
        # 롤백 히스토리 기록
        rollback_record = {
            "timestamp": datetime.now().isoformat(),
            "from_version": old_version,
            "to_version": target_version,
            "reason": reason,
            "previous_metrics": self.health_history[-1].__dict__ if self.health_history else None
        }
        self.rollback_history.append(rollback_record)
        
        logger.info(f"롤백 완료: {old_version} → {target_version}")
        logger.info(f"롤백 사유: {reason}")
        
        return True
    
    def check_health_and_auto_rollback(self) -> Optional[str]:
        """헬스 체크 및 자동 롤백 필요성 판단"""
        if not self.health_history:
            return None
        
        latest = self.health_history[-1]
        current_config = self.versions[self.current_version]
        
        # 지연 시간 초과 체크
        if latest.latency_ms > current_config.max_latency_ms:
            logger.warning(
                f"지연 시간 임계치 초과: {latest.latency_ms}ms > {current_config.max_latency_ms}ms"
            )
            return f"지연 시간 초과: {latest.latency_ms}ms"
        
        # 비용 초과 체크
        if latest.cost_per_request > current_config.max_cost_per_request:
            logger.warning(
                f"비용 임계치 초과: ${latest.cost_per_request:.4f} > ${current_config.max_cost_per_request:.4f}"
            )
            return f"비용 초과: ${latest.cost_per_request:.4f}"
        
        # 오류율 체크 (5% 이상)
        if latest.error_rate > 0.05:
            logger.warning(f"오류율 임계치 초과: {latest.error_rate:.2%}")
            return f"오류율 초과: {latest.error_rate:.2%}"
        
        return None
    
    def send_request(self, prompt: str, system_prompt: str = "") -> Dict[str, Any]:
        """롤백 가능한 요청 전송"""
        start_time = time.time()
        current_config = self.versions[self.current_version]
        
        try:
            response = self.client.chat.completions.create(
                model=current_config.model,
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": prompt}
                ],
                max_tokens=current_config.max_tokens,
                temperature=current_config.temperature
            )
            
            latency_ms = (time.time() - start_time) * 1000
            usage = response.usage
            
            # 토큰 기반 비용 계산
            cost = self._calculate_cost(current_config.model, usage)
            
            # 메트릭 기록
            metric = HealthMetrics(
                timestamp=datetime.now(),
                latency_ms=latency_ms,
                error_rate=0.0,
                cost_per_request=cost,
                success_count=1,
                failure_count=0
            )
            self.health_history.append(metric)
            
            # 자동 롤백 체크 (최근 10개 요청 기준)
            if len(self.health_history) >= 10:
                if self._should_auto_rollback():
                    reason = self.check_health_and_auto_rollback()
                    if reason and self.current_version != "fallback":
                        self.execute_rollback("fallback", reason)
            
            return {
                "content": response.choices[0].message.content,
                "model": current_config.model,
                "latency_ms": latency_ms,
                "cost": cost,
                "version": self.current_version
            }
            
        except Exception as e:
            latency_ms = (time.time() - start_time) * 1000
            
            # 메트릭 기록
            metric = HealthMetrics(
                timestamp=datetime.now(),
                latency_ms=latency_ms,
                error_rate=1.0,
                cost_per_request=0.0,
                success_count=0,
                failure_count=1
            )
            self.health_history.append(metric)
            
            # 자동 롤백 트리거
            if self.current_version != "emergency":
                self.execute_rollback("emergency", f"예외 발생: {str(e)}")
            
            raise
    
    def _calculate_cost(self, model: str, usage) -> float:
        """토큰 사용량 기반 비용 계산 (HolySheep AI 가격표)"""
        pricing = {
            "gpt-4.1": {"input": 0.08, "output": 0.32},  # $8/MTok 입력, $32/MTok 출력
            "claude-sonnet-4-20250514": {"input": 0.015, "output": 0.075},  # $15/MTok (양방향)
            "gemini-2.5-flash": {"input": 0.0025, "output": 0.010},  # $2.50/MTok 입력
            "deepseek-v3.2": {"input": 0.00042, "output": 0.0021},  # $0.42/MTok 입력
        }
        
        p = pricing.get(model, {"input": 0.01, "output": 0.03})
        total_tokens = usage.prompt_tokens + usage.completion_tokens
        
        # 단순화된 계산 (실제 사용량 반영)
        return (usage.prompt_tokens * p["input"] + 
                usage.completion_tokens * p["output"]) / 1000
    
    def _should_auto_rollback(self) -> bool:
        """최근 10개 요청 기반 자동 롤백 판단"""
        recent = self.health_history[-10:]
        
        avg_latency = sum(m.latency_ms for m in recent) / 10
        avg_cost = sum(m.cost_per_request for m in recent) / 10
        avg_error_rate = sum(m.error_rate for m in recent) / 10
        
        current_config = self.versions[self.current_version]
        
        # 3개 지표 중 2개 이상 임계치 초과 시 롤백
        violations = 0
        if avg_latency > current_config.max_latency_ms:
            violations += 1
        if avg_cost > current_config.max_cost_per_request:
            violations += 1
        if avg_error_rate > 0.05:
            violations += 1
        
        return violations >= 2
    
    def get_status_report(self) -> Dict[str, Any]:
        """현재 상태 리포트 생성"""
        recent = self.health_history[-10:] if len(self.health_history) >= 10 else self.health_history
        
        return {
            "current_version": self.current_version,
            "current_model": self.versions[self.current_version].model,
            "total_requests": len(self.health_history),
            "recent_avg_latency_ms": sum(m.latency_ms for m in recent) / len(recent) if recent else 0,
            "recent_avg_cost": sum(m.cost_per_request for m in recent) / len(recent) if recent else 0,
            "recent_avg_error_rate": sum(m.error_rate for m in recent) / len(recent) if recent else 0,
            "rollback_count": len(self.rollback_history),
            "available_versions": list(self.versions.keys())
        }

2단계: HolySheep AI 클라이언트 래퍼 구현

import requests
from typing import List, Dict, Any, Optional
from openai import OpenAI

class HolySheepAIClient:
    """
    HolySheep AI API 래퍼 클래스
    
    HolySheep AI 공식 엔드포인트 사용
    단일 API 키로 GPT-4.1, Claude Sonnet 4.5, Gemini 2.5 Flash, DeepSeek V3.2 통합
    """
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.holysheep.ai/v1"
        
        # OpenAI 호환 클라이언트로 HolySheep API 호출
        self.client = OpenAI(
            api_key=self.api_key,
            base_url=self.base_url
        )
        
    def chat_complete(self, 
                     model: str, 
                     messages: List[Dict[str, str]], 
                     **kwargs) -> Dict[str, Any]:
        """채팅 완성 API 호출"""
        response = self.client.chat.completions.create(
            model=model,
            messages=messages,
            **kwargs
        )
        return response

실제 사용 예시

def main(): # HolySheep AI API 키 설정 api_key = "YOUR_HOLYSHEEP_API_KEY" # HolySheep에서 발급받은 키로 교체 # 롤백 매니저 초기화 manager = RollbackManager(api_key) print("=== HolySheep AI 롤백 시스템 테스트 ===") print(f"현재 버전: {manager.current_version}") print(f"사용 모델: {manager.versions[manager.current_version].model}") print() # 테스트 요청 실행 test_prompts = [ "안녕하세요, AI 서비스 상태를 확인해주세요.", "한국의 주요 관광지에 대해 설명해주세요.", "최신 소프트웨어 개발 트렌드를 알려주세요." ] for i, prompt in enumerate(test_prompts): print(f"\n--- 요청 {i+1} ---") print(f"프롬프트: {prompt}") try: result = manager.send_request( prompt=prompt, system_prompt="당신은 도움이 되는 AI 어시스턴트입니다." ) print(f"응답 모델: {result['model']}") print(f"지연 시간: {result['latency_ms']:.2f}ms") print(f"비용: ${result['cost']:.6f}") print(f"버전: {result['version']}") except Exception as e: print(f"오류 발생: {str(e)}") # 상태 리포트 출력 print("\n" + "="*50) print("=== 최종 상태 리포트 ===") status = manager.get_status_report() for key, value in status.items(): if isinstance(value, float): print(f"{key}: {value:.4f}") else: print(f"{key}: {value}") # 롤백 히스토리 출력 if manager.rollback_history: print("\n=== 롤백 히스토리 ===") for record in manager.rollback_history: print(f"시간: {record['timestamp']}") print(f"변경: {record['from_version']} → {record['to_version']}") print(f"사유: {record['reason']}") print() if __name__ == "__main__": main()

실전 배포 스크립트: Kubernetes 기반 블루-그린 배포

#!/bin/bash

AI 서비스 블루-그린 배포 및 롤백 스크립트

HolySheep AI API 기반 서비스용

set -e

설정

NAMESPACE="ai-services" SERVICE_NAME="ai-chatbot" HOLYSHEEP_API_KEY="${HOLYSHEEP_API_KEY}" REGISTRY="ghcr.io/your-org"

색상 출력

RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" }

헬스 체크 함수

check_health() { local endpoint=$1 local timeout=${2:-30} local start_time=$(date +%s) while true; do local current_time=$(date +%s) local elapsed=$((current_time - start_time)) if [ $elapsed -gt $timeout ]; then log_error "헬스 체크 타임아웃: ${timeout}초" return 1 fi if curl -sf "${endpoint}/health" > /dev/null 2>&1; then log_info "헬스 체크 통과" return 0 fi log_warn "헬스 체크 대기 중... (${elapsed}/${timeout}초)" sleep 2 done }

메트릭 수집 및 롤백 판단

evaluate_rollback_criteria() { local version=$1 local avg_latency=$(kubectl get pods -n $NAMESPACE -l version=$version \ -o jsonpath='{.items[*].metadata.labels.avg_latency_ms}') local error_rate=$(kubectl get pods -n $NAMESPACE -l version=$version \ -o jsonpath='{.items[*].metadata.labels.error_rate}') local cost_per_req=$(kubectl get pods -n $NAMESPACE -l version=$version \ -o jsonpath='{.items[*].metadata.labels.cost_per_request}') # HolySheep AI 기준 임계치 MAX_LATENCY=3000 MAX_ERROR_RATE=0.05 MAX_COST=0.50 log_info "메트릭 평가 중..." log_info " 평균 지연: ${avg_latency}ms (임계치: ${MAX_LATENCY}ms)" log_info " 오류율: ${error_rate} (임계치: ${MAX_ERROR_RATE})" log_info " 요청당 비용: \$${cost_per_req} (임계치: \$${MAX_COST})" # 롤백 조건 체크 if (( $(echo "$avg_latency > $MAX_LATENCY" | bc -l) )) || \ (( $(echo "$error_rate > $MAX_ERROR_RATE" | bc -l) )) || \ (( $(echo "$cost_per_req > $MAX_COST" | bc -l) )); then log_warn "롤백 조건 충족" return 0 else log_info "모든 지표 정상 범위" return 1 fi }

블루-그린 배포 실행

deploy_blue_green() { local new_version=$1 local weight=${2:-10} log_info "블루-그린 배포 시작: v${new_version}" # 새 버전 디플로이먼트를 기존와 분리하여 생성 cat <롤백 실행 rollback_to_version() { local target_version=$1 local reason=${2:-"Manual rollback"} log_warn "롤백 시작: → v${target_version}" log_info "롤백 사유: ${reason}" # 현재 메트릭 기록 kubectl annotate deployment ${SERVICE_NAME}-$(kubectl get deployments -n $NAMESPACE \ -o jsonpath='{.items[*].metadata.labels.version}') \ -n $NAMESPACE \ rollback-reason="${reason}" \ rollback-timestamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)" # 100% 트래픽 이전 kubectl patch virtualservice ${SERVICE_NAME} -n ${NAMESPACE} \ --type='json' \ -p="[{\"op\":\"replace\",\"path\":\"/spec/http/0/routes/0/weight\",\"value\":100},{\"op\":\"replace\",\"path\":\"/spec/http/0/routes/1/weight\",\"value\":0}]" log_info "트래픽 100% v${target_version}로 전환 완료" # 롤백 히스토리 기록 echo "$(date -u +%Y-%m-%dT%H:%M:%SZ),v${target_version},${reason}" >> /var/log/rollback-history.csv }

메인 실행

case "${1:-deploy}" in deploy) deploy_blue_green "${2:-2}" "${3:-10}" ;; rollback) rollback_to_version "${2:-1}" "${3:-Manual}" ;; health-check) check_health "http://${SERVICE_NAME}.${NAMESPACE}.svc.cluster.local" "${3:-30}" ;; evaluate) evaluate_rollback_criteria "${2:-current}" ;; *) echo "Usage: $0 {deploy|rollback|health-check|evaluate} [version] [options]" exit 1 ;; esac

모니터링 대시보드 설정

효과적인 롤백 판단을 위해서는 실시간 모니터링이 필수입니다. Prometheus와 Grafana를 활용한 모니터링 설정 예시입니다.

# prometheus-rules.yml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: ai-service-rollback-rules
  namespace: ai-services
spec:
  groups:
  - name: ai-service-rollback
    interval: 30s
    rules:
    # 지연 시간警报
    - alert: AILatencyHigh
      expr: histogram_quantile(0.95, rate(ai_request_duration_seconds_bucket[5m])) > 3
      for: 2m
      labels:
        severity: warning
      annotations:
        summary: "AI 요청 지연 시간 임계치 초과"
        description: "95번째 백분위 지연 시간: {{ $value }}초 (임계치: 3초)"
        runbook_url: "https://wiki.example.com/runbooks/ai-latency-high"
    
    # 비용警报
    - alert: AIRequestCostHigh
      expr: rate(ai_request_cost_total[1h]) / rate(ai_request_count_total[1h]) > 0.50
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "AI 요청 비용 임계치 초과"
        description: "평균 요청 비용: ${{ $value }} (임계치: $0.50)"
        runbook_url: "https://wiki.example.com/runbooks/ai-cost-high"
    
    # 오류율警报
    - alert: AIErrorRateHigh
      expr: rate(ai_request_errors_total[5m]) / rate(ai_request_count_total[5m]) > 0.05
      for: 3m
      labels:
        severity: critical
      annotations:
        summary: "AI 요청 오류율 임계치 초과"
        description: "오류율: {{ $value | humanizePercentage }} (임계치: 5%)"
        runbook_url: "https://wiki.example.com/runbooks/ai-error-rate-high"
    
    # HolySheep API 연결 실패警报
    - alert: HolySheepAPIConnectionFailed
      expr: rate(holysheep_connection_errors_total[5m]) > 0
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "HolySheep AI API 연결 실패"
        description: "HolySheep API 연결 오류 발생: {{ $value }}회/5분"
    
    # 자동 롤백 추천
    - alert: AIRollbackRecommended
      expr: |
        (histogram_quantile(0.95, rate(ai_request_duration_seconds_bucket[5m])) > 3) and
        (rate(ai_request_cost_total[1h]) / rate(ai_request_count_total[1h]) > 0.50)
      for: 5m
      labels:
        severity: critical
        action: auto-rollback
      annotations:
        summary: "자동 롤백 권장"
        description: "지연 시간과 비용 모두 임계치 초과. 자동 롤백 실행을 권장합니다."
        runbook_url: "https://wiki.example.com/runbooks/ai-auto-rollback"

비용 최적화: HolySheep AI 모델별 비용 비교

모델 입력 비용 ($/MTok) 출력 비용 ($/MTok) 적용 시나리오 평균 지연 시간
GPT-4.1 $8.00 $32.00 고품질 복잡한 작업 800-1200ms
Claude Sonnet 4.5 $15.00 $75.00 긴 컨텍스트 처리 900-1400ms
Gemini 2.5 Flash $2.50 $10.00 빠른 응답, 비용 효율 400-700ms
DeepSeek V3.2 $0.42 $2.10 대량 처리, Economical 600-1000ms

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

오류 1: 롤백 후에도 이전 버전에서 발생했던 문제가 지속되는 경우

증상: 롤백을 실행했음에도 동일한 오류나 성능 저하가 계속 발생합니다.

원인 분석:

해결 코드:

# 롤백 후 전체 상태 초기화 스크립트
def full_state_reset_after_rollback():
    """롤백 후 완전한 상태 초기화"""
    
    # 1. Redis 캐시 플러시
    redis_client = Redis(host='redis.default.svc.cluster.local', port=6379)
    redis_client.flushdb()
    logger.info("Redis 캐시 플러시 완료")
    
    # 2. 메트릭 히스토리 초기화 ( 롤백 시점부터 다시 시작)
    health_history.clear()
    logger.info("헬스 히스토리 초기화 완료")
    
    # 3. 환경 변수 강제 리로드
    current_config = versions[current_version]
    os.environ['MODEL_VERSION'] = current_config.name
    os.environ['MODEL_ENDPOINT'] = f"{base_url}/chat/completions"
    os.environ['MAX_TOKENS'] = str(current_config.max_tokens)
    os.environ['TEMPERATURE'] = str(current_config.temperature)
    
    # 4. 커넥션 풀 리셋
    client.close()
    time.sleep(2)
    client = OpenAI(api_key=api_key, base_url=base_url)
    logger.info("API 커넥션 풀 리셋 완료")
    
    # 5. Kubernetes 환경 변수 리로드 트리거
    subprocess.run([
        'kubectl', 'rollout', 'restart', 
        f'deployment/{service_name}', '-n', namespace
    ])
    logger.info("Kubernetes Pod 리스타트 완료")
    
    return True

오류 2: HolySheep API 연결 타임아웃으로 인한 롤백 무한 루프

증상: HolySheep API 연결이 불안정할 때 롤백이 계속 트리거되어 서비스가 정상 동작하지 않습니다.

원인 분석:

해결 코드:

import time
from threading import Lock
from enum import Enum

class CircuitState(Enum):
    CLOSED = "closed"      # 정상 동작
    OPEN = "open"          # 차단됨
    HALF_OPEN = "half_open" # 테스트 중

class CircuitBreaker:
    """회로 차단기: API 일시 장애 시 무한 롤백 방지"""
    
    def __init__(self, 
                 failure_threshold: int = 5,
                 recovery_timeout: int = 60,
                 success_threshold: int = 3):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.success_threshold = success_threshold
        
        self.failure_count = 0
        self.success_count = 0
        self.last_failure_time = None
        self.state = CircuitState.CLOSED
        self.lock = Lock()
    
    def call(self, func, *args, **kwargs):
        """회로 차단기 보호 함수 호출"""
        with self.lock:
            if self.state == CircuitState.OPEN:
                # recovery_timeout 경과 확인
                if time.time() - self.last_failure_time >= self.recovery_timeout:
                    self.state = CircuitState.HALF_OPEN
                    self.success_count = 0
                    logger.info("회로 차단기: HALF_OPEN 상태 전환")
                else:
                    raise CircuitOpenException(
                        f"회로 차단기 OPEN: {self.recovery_timeout}초 후 재시도"
                    )
            
            try:
                result = func(*args, **kwargs)
                self._on_success()
                return result
                
            except Exception as e:
                self._on_failure()
                raise
    
    def _on_success(self):
        self.failure_count = 0