# 콜백 API 가이드 ## 📋 개요 전략 실행 시 `callback_url` 파라미터를 지정하면, 전략 실행이 완료된 후 해당 URL로 결과를 POST 메서드로 자동 전송합니다. --- ## 🚀 사용 방법 ### 1. 기본 사용 예제 ```bash curl -X POST http://localhost:8000/strategies/execute/ \ -H "Content-Type: application/json" \ -d '{ "strategy_name": "BoldAssetAllocation", "parameters": { "variant": "BAA-G4", "initial_capital": 100000 }, "callback_url": "https://your-server.com/webhook/strategy-result" }' ``` **응답:** ```json { "execution_id": 123, "status": "pending", "message": "Strategy execution started", "callback_url": "https://your-server.com/webhook/strategy-result" } ``` ### 2. 콜백 URL 없이 실행 (기존 방식) ```bash curl -X POST http://localhost:8000/strategies/execute/ \ -H "Content-Type: application/json" \ -d '{ "strategy_name": "MovingAverageCrossover", "parameters": { "short_window": 20, "long_window": 50 } }' ``` --- ## 📨 콜백 페이로드 전략 실행이 완료되면 다음과 같은 JSON 데이터가 `callback_url`로 POST 요청됩니다. ### 성공 시 (status: completed) ```json { "execution_id": 123, "strategy": "BoldAssetAllocation", "version": "1.0.0", "status": "completed", "started_at": "2025-10-04T12:00:00.000Z", "completed_at": "2025-10-04T12:00:15.000Z", "execution_parameters": { "variant": "BAA-G4", "initial_capital": 100000, "use_real_data": false }, "result": { "strategy": "BoldAssetAllocation", "variant": "BAA-G4", "portfolio": { "offensive_assets": ["VTI", "VEA"], "weights": { "VTI": 0.5, "VEA": 0.5 } }, "final_value": 125000, "return": 0.25 } } ``` ### 실패 시 (status: failed) ```json { "execution_id": 124, "strategy": "BoldAssetAllocation", "version": "1.0.0", "status": "failed", "started_at": "2025-10-04T12:05:00.000Z", "completed_at": "2025-10-04T12:05:02.000Z", "execution_parameters": { "variant": "INVALID" }, "error_message": "Unknown variant: INVALID. Available variants: BAA-G12, BAA-G4, ..." } ``` --- ## 🔍 실행 상태 확인 콜백 전송 정보를 포함한 실행 상태 조회: ```bash curl http://localhost:8000/executions/123/ ``` **응답:** ```json { "execution_id": 123, "strategy": "BoldAssetAllocation", "version": "1.0.0", "status": "completed", "started_at": "2025-10-04T12:00:00.000Z", "completed_at": "2025-10-04T12:00:15.000Z", "execution_parameters": {...}, "result": {...}, "callback": { "url": "https://your-server.com/webhook/strategy-result", "sent": true, "sent_at": "2025-10-04T12:00:15.500Z", "response": { "status_code": 200, "response_text": "{\"status\": \"received\"}", "headers": { "Content-Type": "application/json" } } } } ``` --- ## 🛠️ 콜백 서버 구현 예제 ### Python (Flask) ```python from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/webhook/strategy-result', methods=['POST']) def strategy_callback(): data = request.json execution_id = data['execution_id'] status = data['status'] if status == 'completed': result = data['result'] print(f"전략 실행 완료 (ID: {execution_id})") print(f"최종 수익률: {result.get('return', 0) * 100:.2f}%") # 결과를 데이터베이스에 저장하거나 다른 처리 # save_to_database(data) elif status == 'failed': error_message = data['error_message'] print(f"전략 실행 실패 (ID: {execution_id}): {error_message}") # 에러 알림 전송 # send_error_notification(data) return jsonify({'status': 'received'}), 200 if __name__ == '__main__': app.run(port=8888) ``` ### Node.js (Express) ```javascript const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhook/strategy-result', (req, res) => { const { execution_id, status, result, error_message } = req.body; if (status === 'completed') { console.log(`전략 실행 완료 (ID: ${execution_id})`); console.log(`최종 수익률: ${(result.return * 100).toFixed(2)}%`); // 결과 처리 // saveToDatabase(req.body); } else if (status === 'failed') { console.error(`전략 실행 실패 (ID: ${execution_id}): ${error_message}`); // 에러 처리 // sendErrorNotification(req.body); } res.json({ status: 'received' }); }); app.listen(8888, () => { console.log('콜백 서버 시작: http://localhost:8888'); }); ``` ### Django View ```python from django.views.decorators.csrf import csrf_exempt from django.http import JsonResponse import json @csrf_exempt def strategy_callback(request): if request.method == 'POST': data = json.loads(request.body) execution_id = data['execution_id'] status = data['status'] if status == 'completed': # 결과 처리 result = data['result'] print(f"전략 실행 완료: {result}") elif status == 'failed': # 에러 처리 error = data['error_message'] print(f"전략 실행 실패: {error}") return JsonResponse({'status': 'received'}) return JsonResponse({'error': 'Method not allowed'}, status=405) ``` --- ## 🧪 테스트 방법 ### 1. 자동 테스트 스크립트 사용 ```bash python test_callback.py ``` 이 스크립트는: - 로컬에 콜백 수신 서버를 자동으로 시작 - 전략 실행 요청 (콜백 URL 포함) - 콜백 수신 및 결과 출력 ### 2. 수동 테스트 **터미널 1 - 콜백 서버:** ```bash python -m http.server 8888 ``` **터미널 2 - 전략 실행:** ```bash curl -X POST http://localhost:8000/strategies/execute/ \ -H "Content-Type: application/json" \ -d '{ "strategy_name": "MovingAverageCrossover", "parameters": { "short_window": 10, "long_window": 30 }, "callback_url": "http://localhost:8888/callback" }' ``` ### 3. 외부 테스트 도구 **webhook.site 사용:** 1. https://webhook.site 방문 2. 생성된 고유 URL 복사 3. 해당 URL을 `callback_url`로 사용 ```bash curl -X POST http://localhost:8000/strategies/execute/ \ -H "Content-Type: application/json" \ -d '{ "strategy_name": "BoldAssetAllocation", "parameters": {"variant": "BAA-G4"}, "callback_url": "https://webhook.site/your-unique-id" }' ``` 4. webhook.site에서 실시간으로 수신된 데이터 확인 --- ## ⚙️ 설정 및 동작 ### 콜백 타임아웃 콜백 POST 요청은 **10초 타임아웃**이 설정되어 있습니다. ### 재시도 정책 현재 콜백은 **1회만 시도**합니다. 실패 시 재시도하지 않습니다. 실패 시 `callback_response`에 에러 정보가 저장됩니다: ```json { "callback": { "url": "https://unavailable.com/webhook", "sent": false, "sent_at": null, "response": { "error": "Connection refused" } } } ``` ### 보안 고려사항 1. **HTTPS 사용 권장**: 프로덕션 환경에서는 HTTPS 콜백 URL 사용 2. **서명 검증**: 콜백 요청의 진위 확인을 위해 서명 메커니즘 고려 3. **IP 화이트리스트**: 알려진 IP에서만 콜백 수신 4. **Rate Limiting**: 과도한 요청 방지 --- ## 📊 활용 사례 ### 1. Slack 알림 ```python import requests @app.route('/webhook/strategy-result', methods=['POST']) def strategy_callback(): data = request.json if data['status'] == 'completed': result = data['result'] message = f"✅ 전략 실행 완료\n" \ f"전략: {data['strategy']}\n" \ f"수익률: {result.get('return', 0) * 100:.2f}%" else: message = f"❌ 전략 실행 실패\n" \ f"오류: {data['error_message']}" # Slack Webhook requests.post( 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL', json={'text': message} ) return jsonify({'status': 'ok'}) ``` ### 2. 이메일 전송 ```python from django.core.mail import send_mail @csrf_exempt def strategy_callback(request): data = json.loads(request.body) if data['status'] == 'completed': subject = f"전략 실행 완료: {data['strategy']}" message = f"수익률: {data['result']['return'] * 100:.2f}%" send_mail( subject, message, 'noreply@yoursite.com', ['admin@yoursite.com'], ) return JsonResponse({'status': 'sent'}) ``` ### 3. 데이터베이스 저장 ```python @csrf_exempt def strategy_callback(request): data = json.loads(request.body) StrategyResult.objects.create( execution_id=data['execution_id'], strategy_name=data['strategy'], status=data['status'], result=data.get('result'), error=data.get('error_message'), completed_at=data.get('completed_at') ) return JsonResponse({'status': 'saved'}) ``` --- ## 🐛 문제 해결 ### 콜백이 전송되지 않음 **확인사항:** 1. `callback_url`이 올바른 형식인지 확인 2. 콜백 서버가 실행 중인지 확인 3. 방화벽/보안 그룹 설정 확인 4. 실행 상태 API에서 `callback.response` 확인 ```bash curl http://localhost:8000/executions/{ID}/ | python -m json.tool ``` ### 타임아웃 발생 콜백 서버의 응답 시간이 10초를 초과하는 경우: **해결책:** - 콜백 핸들러에서 빠르게 200 응답 후 백그라운드 처리 - 타임아웃 값 조정 필요 시 `views.py`의 `timeout=10` 수정 ### localhost 콜백이 작동하지 않음 Docker 컨테이너 내부에서 실행 중인 경우: **해결책:** - `localhost` 대신 호스트 IP 사용 - Docker: `host.docker.internal` 사용 (Mac/Windows) - ngrok 등으로 로컬 서버 외부 노출 --- ## 📚 참고 - **API 전체 가이드**: `API_USAGE_GUIDE.md` - **BAA 전략 설명**: `BAA_STRATEGY_README.md` - **배포 가이드**: `DEPLOYMENT_GUIDE.md` --- ## 🔄 업데이트 로그 | 날짜 | 변경 내용 | |------|-----------| | 2025-10-04 | 콜백 기능 최초 구현 |