박세원 c9614263c0 백엔드 AI 서비스와 프론트엔드 완전 연동
- AI 서비스 API 클라이언트 추가 (aiApi.ts)
- Event 서비스 API 클라이언트 추가 (eventApi.ts)
- RecommendationStep에서 실제 API 호출로 변경
- Job 폴링 메커니즘 구현 (5초 간격)
- ContentPreviewStep의 Mock 데이터 제거
- Props를 통한 eventId 전달 구조 개선
- ApprovalStep의 타입 오류 수정
- 모든 Mock/Static 데이터 제거 완료

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 13:49:45 +09:00

179 lines
4.1 KiB
TypeScript

import axios, { AxiosInstance } from 'axios';
// AI Service API 클라이언트
const AI_API_BASE_URL = process.env.NEXT_PUBLIC_AI_HOST || 'http://localhost:8083';
export const aiApiClient: AxiosInstance = axios.create({
baseURL: AI_API_BASE_URL,
timeout: 300000, // AI 생성은 최대 5분
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor
aiApiClient.interceptors.request.use(
(config) => {
console.log('🤖 AI API Request:', {
method: config.method?.toUpperCase(),
url: config.url,
baseURL: config.baseURL,
data: config.data,
});
const token = localStorage.getItem('accessToken');
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
console.error('❌ AI API Request Error:', error);
return Promise.reject(error);
}
);
// Response interceptor
aiApiClient.interceptors.response.use(
(response) => {
console.log('✅ AI API Response:', {
status: response.status,
url: response.config.url,
data: response.data,
});
return response;
},
(error) => {
console.error('❌ AI API Error:', {
message: error.message,
status: error.response?.status,
url: error.config?.url,
data: error.response?.data,
});
return Promise.reject(error);
}
);
// Types
export interface TrendKeyword {
keyword: string;
relevance: number;
description: string;
}
export interface TrendAnalysis {
industryTrends: TrendKeyword[];
regionalTrends: TrendKeyword[];
seasonalTrends: TrendKeyword[];
}
export interface ExpectedMetrics {
newCustomers: {
min: number;
max: number;
};
repeatVisits?: {
min: number;
max: number;
};
revenueIncrease: {
min: number;
max: number;
};
roi: {
min: number;
max: number;
};
socialEngagement?: {
estimatedPosts: number;
estimatedReach: number;
};
}
export interface EventRecommendation {
optionNumber: number;
concept: string;
title: string;
description: string;
targetAudience: string;
duration: {
recommendedDays: number;
recommendedPeriod?: string;
};
mechanics: {
type: 'DISCOUNT' | 'GIFT' | 'STAMP' | 'EXPERIENCE' | 'LOTTERY' | 'COMBO';
details: string;
};
promotionChannels: string[];
estimatedCost: {
min: number;
max: number;
breakdown?: {
material?: number;
promotion?: number;
discount?: number;
};
};
expectedMetrics: ExpectedMetrics;
differentiator: string;
}
export interface AIRecommendationResult {
eventId: string;
trendAnalysis: TrendAnalysis;
recommendations: EventRecommendation[];
generatedAt: string;
expiresAt: string;
aiProvider: 'CLAUDE' | 'GPT4';
}
export interface JobStatusResponse {
jobId: string;
status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
progress: number;
message: string;
eventId?: string;
createdAt: string;
startedAt?: string;
completedAt?: string;
failedAt?: string;
errorMessage?: string;
retryCount?: number;
processingTimeMs?: number;
}
export interface HealthCheckResponse {
status: 'UP' | 'DOWN' | 'DEGRADED';
timestamp: string;
services: {
kafka: 'UP' | 'DOWN';
redis: 'UP' | 'DOWN';
claude_api: 'UP' | 'DOWN' | 'CIRCUIT_OPEN';
gpt4_api?: 'UP' | 'DOWN' | 'CIRCUIT_OPEN';
circuit_breaker: 'CLOSED' | 'OPEN' | 'HALF_OPEN';
};
}
// API Functions
export const aiApi = {
// 헬스체크
healthCheck: async (): Promise<HealthCheckResponse> => {
const response = await aiApiClient.get<HealthCheckResponse>('/health');
return response.data;
},
// Job 상태 조회 (Internal API)
getJobStatus: async (jobId: string): Promise<JobStatusResponse> => {
const response = await aiApiClient.get<JobStatusResponse>(`/internal/jobs/${jobId}/status`);
return response.data;
},
// AI 추천 결과 조회 (Internal API)
getRecommendations: async (eventId: string): Promise<AIRecommendationResult> => {
const response = await aiApiClient.get<AIRecommendationResult>(`/internal/recommendations/${eventId}`);
return response.data;
},
};
export default aiApi;