UNPKG

@restnfeel/agentc-starter-kit

Version:

한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템

423 lines (371 loc) 11.5 kB
interface MediaAnalytics { totalFiles: number; totalSize: number; fileTypes: Record<string, number>; uploadTrends: { date: string; uploads: number; size: number; }[]; popularFiles: { id: string; fileName: string; views: number; downloads: number; }[]; storageUsage: { local: number; cdn: number; total: number; }; performanceMetrics: { averageUploadTime: number; averageProcessingTime: number; successRate: number; }; } interface MediaUsageReport { period: "daily" | "weekly" | "monthly"; startDate: string; endDate: string; analytics: MediaAnalytics; insights: string[]; recommendations: string[]; } class MediaAnalyticsService { private analytics: MediaAnalytics | null = null; private lastUpdate: Date | null = null; private cache: Map<string, any> = new Map(); /** * 미디어 분석 데이터 수집 */ async collectAnalytics(): Promise<MediaAnalytics> { try { // 기본 통계 수집 const totalFiles = await this.getTotalFileCount(); const totalSize = await this.getTotalStorageSize(); const fileTypes = await this.getFileTypeDistribution(); const uploadTrends = await this.getUploadTrends(); const popularFiles = await this.getPopularFiles(); const storageUsage = await this.getStorageUsage(); const performanceMetrics = await this.getPerformanceMetrics(); this.analytics = { totalFiles, totalSize, fileTypes, uploadTrends, popularFiles, storageUsage, performanceMetrics, }; this.lastUpdate = new Date(); return this.analytics; } catch (error) { console.error("Failed to collect analytics:", error); throw new Error("분석 데이터 수집에 실패했습니다."); } } /** * 총 파일 수 조회 */ private async getTotalFileCount(): Promise<number> { // 실제 구현에서는 데이터베이스 쿼리 // Mock 데이터 반환 return 1247; } /** * 총 저장소 크기 조회 (bytes) */ private async getTotalStorageSize(): Promise<number> { // 실제 구현에서는 데이터베이스 쿼리 // Mock 데이터 반환 return 5.2 * 1024 * 1024 * 1024; // 5.2GB } /** * 파일 타입별 분포 조회 */ private async getFileTypeDistribution(): Promise<Record<string, number>> { // 실제 구현에서는 데이터베이스 쿼리 // Mock 데이터 반환 return { "image/jpeg": 523, "image/png": 387, "image/webp": 215, "image/gif": 89, "video/mp4": 23, "video/webm": 8, "application/pdf": 2, }; } /** * 업로드 트렌드 조회 (최근 30일) */ private async getUploadTrends(): Promise< { date: string; uploads: number; size: number }[] > { // 실제 구현에서는 데이터베이스 쿼리 // Mock 데이터 생성 const trends = []; const now = new Date(); for (let i = 29; i >= 0; i--) { const date = new Date(now); date.setDate(date.getDate() - i); trends.push({ date: date.toISOString().split("T")[0], uploads: Math.floor(Math.random() * 50) + 10, size: Math.floor(Math.random() * 100 * 1024 * 1024) + 50 * 1024 * 1024, // 50-150MB }); } return trends; } /** * 인기 파일 조회 */ private async getPopularFiles(): Promise< { id: string; fileName: string; views: number; downloads: number }[] > { // 실제 구현에서는 데이터베이스 쿼리 // Mock 데이터 반환 return [ { id: "1", fileName: "hero-banner.jpg", views: 1523, downloads: 89 }, { id: "2", fileName: "product-image.png", views: 1247, downloads: 67 }, { id: "3", fileName: "logo-transparent.png", views: 998, downloads: 234 }, { id: "4", fileName: "demo-video.mp4", views: 756, downloads: 45 }, { id: "5", fileName: "screenshot.jpg", views: 623, downloads: 23 }, ]; } /** * 저장소 사용량 조회 */ private async getStorageUsage(): Promise<{ local: number; cdn: number; total: number; }> { // 실제 구현에서는 실제 저장소 사용량 조회 // Mock 데이터 반환 const local = 2.1 * 1024 * 1024 * 1024; // 2.1GB const cdn = 3.1 * 1024 * 1024 * 1024; // 3.1GB return { local, cdn, total: local + cdn, }; } /** * 성능 지표 조회 */ private async getPerformanceMetrics(): Promise<{ averageUploadTime: number; averageProcessingTime: number; successRate: number; }> { // 실제 구현에서는 성능 로그 분석 // Mock 데이터 반환 return { averageUploadTime: 2.3, // seconds averageProcessingTime: 4.7, // seconds successRate: 98.5, // percentage }; } /** * 사용량 보고서 생성 */ async generateUsageReport( period: "daily" | "weekly" | "monthly" = "monthly" ): Promise<MediaUsageReport> { const analytics = await this.collectAnalytics(); const { startDate, endDate } = this.getPeriodDates(period); const insights = this.generateInsights(analytics); const recommendations = this.generateRecommendations(analytics); return { period, startDate, endDate, analytics, insights, recommendations, }; } /** * 기간별 날짜 계산 */ private getPeriodDates(period: "daily" | "weekly" | "monthly"): { startDate: string; endDate: string; } { const endDate = new Date(); const startDate = new Date(); switch (period) { case "daily": startDate.setDate(startDate.getDate() - 1); break; case "weekly": startDate.setDate(startDate.getDate() - 7); break; case "monthly": startDate.setMonth(startDate.getMonth() - 1); break; } return { startDate: startDate.toISOString().split("T")[0], endDate: endDate.toISOString().split("T")[0], }; } /** * 인사이트 생성 */ private generateInsights(analytics: MediaAnalytics): string[] { const insights: string[] = []; // 파일 타입 분석 const totalFiles = analytics.totalFiles; const imageFiles = Object.entries(analytics.fileTypes) .filter(([type]) => type.startsWith("image/")) .reduce((sum, [, count]) => sum + count, 0); const imagePercentage = ((imageFiles / totalFiles) * 100).toFixed(1); insights.push(`이미지 파일이 전체의 ${imagePercentage}%를 차지합니다.`); // 저장소 사용량 분석 const { local, cdn, total } = analytics.storageUsage; const cdnPercentage = ((cdn / total) * 100).toFixed(1); insights.push(`CDN 저장소 사용률이 ${cdnPercentage}%입니다.`); // 성능 분석 if (analytics.performanceMetrics.successRate < 95) { insights.push( `업로드 성공률이 ${analytics.performanceMetrics.successRate}%로 개선이 필요합니다.` ); } // 트렌드 분석 const recentTrend = analytics.uploadTrends.slice(-7); const avgRecentUploads = recentTrend.reduce((sum, day) => sum + day.uploads, 0) / 7; const earlierTrend = analytics.uploadTrends.slice(-14, -7); const avgEarlierUploads = earlierTrend.reduce((sum, day) => sum + day.uploads, 0) / 7; const trendChange = ( ((avgRecentUploads - avgEarlierUploads) / avgEarlierUploads) * 100 ).toFixed(1); if (parseFloat(trendChange) > 0) { insights.push( `최근 일주일 업로드가 이전 주 대비 ${trendChange}% 증가했습니다.` ); } else { insights.push( `최근 일주일 업로드가 이전 주 대비 ${Math.abs( parseFloat(trendChange) )}% 감소했습니다.` ); } return insights; } /** * 권장사항 생성 */ private generateRecommendations(analytics: MediaAnalytics): string[] { const recommendations: string[] = []; // 저장소 최적화 const storageUsage = analytics.storageUsage; const storageUsagePercentage = (storageUsage.total / (10 * 1024 * 1024 * 1024)) * 100; // 10GB 한도 가정 if (storageUsagePercentage > 80) { recommendations.push( "저장소 사용량이 80%를 초과했습니다. 불필요한 파일 정리를 권장합니다." ); } // 파일 형식 최적화 const jpegCount = analytics.fileTypes["image/jpeg"] || 0; const webpCount = analytics.fileTypes["image/webp"] || 0; if (jpegCount > webpCount * 2) { recommendations.push( "WebP 형식 사용을 늘려 파일 크기를 줄이고 로딩 속도를 개선하세요." ); } // 성능 최적화 if (analytics.performanceMetrics.averageUploadTime > 5) { recommendations.push( "평균 업로드 시간이 길어졌습니다. 네트워크 연결이나 서버 성능을 확인하세요." ); } // CDN 활용 const cdnUsagePercentage = (storageUsage.cdn / storageUsage.total) * 100; if (cdnUsagePercentage < 70) { recommendations.push( "CDN 사용률을 늘려 전 세계 사용자의 로딩 속도를 개선하세요." ); } // 보안 권장사항 recommendations.push( "정기적으로 미사용 파일을 정리하고 접근 권한을 검토하세요." ); return recommendations; } /** * 실시간 통계 조회 */ async getRealTimeStats(): Promise<{ activeUploads: number; processingJobs: number; recentUploads: number; errorRate: number; }> { // 실제 구현에서는 실시간 모니터링 데이터 조회 // Mock 데이터 반환 return { activeUploads: 3, processingJobs: 7, recentUploads: 45, // 최근 1시간 errorRate: 1.2, // percentage }; } /** * 캐시된 분석 데이터 조회 */ getCachedAnalytics(): MediaAnalytics | null { const cacheExpiry = 30 * 60 * 1000; // 30분 if ( this.lastUpdate && Date.now() - this.lastUpdate.getTime() < cacheExpiry ) { return this.analytics; } return null; } /** * 분석 데이터 내보내기 */ async exportAnalytics(format: "json" | "csv" = "json"): Promise<string> { const analytics = await this.collectAnalytics(); if (format === "csv") { return this.convertToCSV(analytics); } return JSON.stringify(analytics, null, 2); } /** * CSV 형식으로 변환 */ private convertToCSV(analytics: MediaAnalytics): string { const headers = ["Metric", "Value"]; const rows = [ ["Total Files", analytics.totalFiles.toString()], [ "Total Size (GB)", (analytics.totalSize / (1024 * 1024 * 1024)).toFixed(2), ], ["Success Rate (%)", analytics.performanceMetrics.successRate.toString()], [ "Average Upload Time (s)", analytics.performanceMetrics.averageUploadTime.toString(), ], ]; return [headers, ...rows].map((row) => row.join(",")).join("\n"); } } // 싱글톤 인스턴스 생성 let analyticsServiceInstance: MediaAnalyticsService | null = null; export function getMediaAnalyticsService(): MediaAnalyticsService { if (!analyticsServiceInstance) { analyticsServiceInstance = new MediaAnalyticsService(); } return analyticsServiceInstance; } export type { MediaAnalytics, MediaUsageReport };