Analytics 테스트 페이지 디버그 로그 추가

- 이벤트 기간 계산 함수에 상세 디버그 로그 추가
- 차트 데이터 생성 함수에 필터링 과정 로그 추가
- Timeline dataPoints 구조 확인을 위한 콘솔 출력 추가
- ROI 필드 매핑 검증을 위한 로그 추가
This commit is contained in:
Hyowon Yang
2025-10-29 19:33:19 +09:00
parent 37e5a76c50
commit abceae6e2a
8 changed files with 1968 additions and 212 deletions
+142
View File
@@ -0,0 +1,142 @@
import { analyticsClient } from './analyticsClient';
import type {
ApiResponse,
UserAnalyticsDashboardResponse,
AnalyticsDashboardResponse,
UserTimelineAnalyticsResponse,
TimelineAnalyticsResponse,
UserRoiAnalyticsResponse,
RoiAnalyticsResponse,
UserChannelAnalyticsResponse,
ChannelAnalyticsResponse,
AnalyticsQueryParams,
TimelineQueryParams,
ChannelQueryParams,
RoiQueryParams,
} from '../model/types';
const API_VERSION = process.env.NEXT_PUBLIC_API_VERSION || 'v1';
/**
* Analytics API Service
* 실시간 효과 측정 및 통합 대시보드 API
*/
export const analyticsApi = {
// ============= User Analytics (사용자 전체 이벤트 통합) =============
/**
* 사용자 전체 성과 대시보드 조회
*/
getUserAnalytics: async (
userId: string,
params?: AnalyticsQueryParams
): Promise<UserAnalyticsDashboardResponse> => {
const response = await analyticsClient.get<ApiResponse<UserAnalyticsDashboardResponse>>(
`/api/${API_VERSION}/users/${userId}/analytics`,
{ params }
);
return response.data.data;
},
/**
* 사용자 전체 시간대별 참여 추이
*/
getUserTimelineAnalytics: async (
userId: string,
params?: TimelineQueryParams
): Promise<UserTimelineAnalyticsResponse> => {
const response = await analyticsClient.get<ApiResponse<UserTimelineAnalyticsResponse>>(
`/api/${API_VERSION}/users/${userId}/analytics/timeline`,
{ params }
);
return response.data.data;
},
/**
* 사용자 전체 ROI 상세 분석
*/
getUserRoiAnalytics: async (
userId: string,
params?: AnalyticsQueryParams & RoiQueryParams
): Promise<UserRoiAnalyticsResponse> => {
const response = await analyticsClient.get<ApiResponse<UserRoiAnalyticsResponse>>(
`/api/${API_VERSION}/users/${userId}/analytics/roi`,
{ params }
);
return response.data.data;
},
/**
* 사용자 전체 채널별 성과 분석
*/
getUserChannelAnalytics: async (
userId: string,
params?: ChannelQueryParams
): Promise<UserChannelAnalyticsResponse> => {
const response = await analyticsClient.get<ApiResponse<UserChannelAnalyticsResponse>>(
`/api/${API_VERSION}/users/${userId}/analytics/channels`,
{ params }
);
return response.data.data;
},
// ============= Event Analytics (특정 이벤트 분석) =============
/**
* 이벤트 성과 대시보드 조회
*/
getEventAnalytics: async (
eventId: string,
params?: AnalyticsQueryParams
): Promise<AnalyticsDashboardResponse> => {
const response = await analyticsClient.get<ApiResponse<AnalyticsDashboardResponse>>(
`/api/${API_VERSION}/events/${eventId}/analytics`,
{ params }
);
return response.data.data;
},
/**
* 이벤트 시간대별 참여 추이
*/
getEventTimelineAnalytics: async (
eventId: string,
params?: TimelineQueryParams
): Promise<TimelineAnalyticsResponse> => {
const response = await analyticsClient.get<ApiResponse<TimelineAnalyticsResponse>>(
`/api/${API_VERSION}/events/${eventId}/analytics/timeline`,
{ params }
);
return response.data.data;
},
/**
* 이벤트 ROI 상세 분석
*/
getEventRoiAnalytics: async (
eventId: string,
params?: RoiQueryParams
): Promise<RoiAnalyticsResponse> => {
const response = await analyticsClient.get<ApiResponse<RoiAnalyticsResponse>>(
`/api/${API_VERSION}/events/${eventId}/analytics/roi`,
{ params }
);
return response.data.data;
},
/**
* 이벤트 채널별 성과 분석
*/
getEventChannelAnalytics: async (
eventId: string,
params?: ChannelQueryParams
): Promise<ChannelAnalyticsResponse> => {
const response = await analyticsClient.get<ApiResponse<ChannelAnalyticsResponse>>(
`/api/${API_VERSION}/events/${eventId}/analytics/channels`,
{ params }
);
return response.data.data;
},
};
export default analyticsApi;
@@ -0,0 +1,67 @@
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';
const ANALYTICS_HOST =
process.env.NEXT_PUBLIC_ANALYTICS_HOST || 'http://localhost:8086';
export const analyticsClient: AxiosInstance = axios.create({
baseURL: ANALYTICS_HOST,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor - JWT 토큰 추가
analyticsClient.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
console.log('🚀 Analytics API Request:', {
method: config.method?.toUpperCase(),
url: config.url,
baseURL: config.baseURL,
params: config.params,
});
const token = localStorage.getItem('accessToken');
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`;
console.log('🔑 Token added to analytics request');
}
return config;
},
(error: AxiosError) => {
console.error('❌ Analytics Request Error:', error);
return Promise.reject(error);
}
);
// Response interceptor - 에러 처리
analyticsClient.interceptors.response.use(
(response) => {
console.log('✅ Analytics API Response:', {
status: response.status,
url: response.config.url,
data: response.data,
});
return response;
},
(error: AxiosError) => {
console.error('❌ Analytics API Error:', {
message: error.message,
status: error.response?.status,
statusText: error.response?.statusText,
url: error.config?.url,
data: error.response?.data,
});
if (error.response?.status === 401) {
console.warn('🔒 401 Unauthorized - Redirecting to login');
localStorage.removeItem('accessToken');
if (typeof window !== 'undefined') {
window.location.href = '/login';
}
}
return Promise.reject(error);
}
);
export default analyticsClient;
+3
View File
@@ -0,0 +1,3 @@
export { analyticsApi } from './analyticsApi';
export { analyticsClient } from './analyticsClient';
export * from '../model/types';
+2
View File
@@ -0,0 +1,2 @@
export * from './api';
export * from './model/types';
+295
View File
@@ -0,0 +1,295 @@
/**
* Analytics API Types
* Based on Analytics Service OpenAPI Specification
*/
// ============= Common Types =============
export interface PeriodInfo {
startDate: string;
endDate: string;
durationDays: number;
}
export interface SocialInteractionStats {
likes: number;
comments: number;
shares: number;
}
// ============= Summary Types =============
export interface AnalyticsSummary {
participants: number;
participantsDelta: number;
totalViews: number;
totalReach: number;
engagementRate: number;
conversionRate: number;
averageEngagementTime: number;
targetRoi: number;
socialInteractions?: SocialInteractionStats;
}
export interface ChannelSummary {
channel: string;
views: number;
participants: number;
engagementRate: number;
conversionRate: number;
roi: number;
}
export interface RoiSummary {
totalCost: number;
expectedRevenue: number;
netProfit: number;
roi: number;
costPerAcquisition: number;
}
export interface EventPerformanceSummary {
eventId: string;
eventTitle: string;
participants: number;
views: number;
roi: number;
status: string;
}
// ============= Timeline Types =============
export interface TimelineDataPoint {
timestamp: string;
participants: number;
views: number;
engagement: number;
conversions: number;
cumulativeParticipants: number;
}
export interface PeakTimeInfo {
timestamp: string;
metric: string;
value: number;
description: string;
}
export interface TrendAnalysis {
overallTrend: string;
growthRate: number;
projectedParticipants: number;
peakPeriod: string;
}
// ============= ROI Types =============
export interface InvestmentDetails {
prizeCost: number;
contentCreation: number;
distribution: number;
operation: number;
total: number;
breakdown?: Record<string, unknown>[];
}
export interface RevenueDetails {
directSales: number;
expectedSales: number;
brandValue: number;
newCustomerRevenue: number;
total: number;
}
export interface RoiCalculation {
netProfit: number;
roiPercentage: number;
breakEvenPoint?: string;
paybackPeriod: number;
}
export interface CostEfficiency {
costPerParticipant: number;
costPerConversion: number;
costPerView: number;
revenuePerParticipant: number;
}
export interface RevenueProjection {
currentRevenue: number;
projectedFinalRevenue: number;
confidenceLevel: number;
basedOn: string;
}
// ============= Channel Types =============
export interface VoiceCallStats {
totalCalls: number;
completedCalls: number;
averageDuration: number;
completionRate: number;
}
export interface ChannelMetrics {
impressions: number;
views: number;
clicks: number;
participants: number;
conversions: number;
socialInteractions?: SocialInteractionStats;
voiceCallStats?: VoiceCallStats;
}
export interface ChannelPerformance {
clickThroughRate: number;
engagementRate: number;
conversionRate: number;
averageEngagementTime: number;
bounceRate: number;
}
export interface ChannelCosts {
distributionCost: number;
costPerView: number;
costPerClick: number;
costPerAcquisition: number;
roi: number;
}
export interface ChannelAnalytics {
channelName: string;
channelType: string;
metrics: ChannelMetrics;
performance: ChannelPerformance;
costs: ChannelCosts;
externalApiStatus?: string;
}
export interface ChannelComparison {
bestPerforming: Record<string, string>;
averageMetrics: Record<string, number>;
}
// ============= Dashboard Response Types =============
export interface UserAnalyticsDashboardResponse {
userId: string;
period: PeriodInfo;
totalEvents: number;
activeEvents: number;
overallSummary: AnalyticsSummary;
channelPerformance: ChannelSummary[];
overallRoi: RoiSummary;
eventPerformances: EventPerformanceSummary[];
lastUpdatedAt: string;
dataSource: string;
}
export interface AnalyticsDashboardResponse {
eventId: string;
eventTitle: string;
period: PeriodInfo;
summary: AnalyticsSummary;
channelPerformance: ChannelSummary[];
roi: RoiSummary;
lastUpdatedAt: string;
dataSource: string;
}
export interface TimelineAnalyticsResponse {
eventId: string;
interval: string;
dataPoints: TimelineDataPoint[];
trends: TrendAnalysis;
peakTimes: PeakTimeInfo[];
lastUpdatedAt: string;
}
export interface UserTimelineAnalyticsResponse {
userId: string;
period: PeriodInfo;
totalEvents: number;
interval: string;
dataPoints: TimelineDataPoint[];
trend: TrendAnalysis;
peakTime: PeakTimeInfo;
lastUpdatedAt: string;
dataSource: string;
}
export interface RoiAnalyticsResponse {
eventId: string;
investment: InvestmentDetails;
revenue: RevenueDetails;
roi: RoiCalculation;
costEfficiency: CostEfficiency;
projection: RevenueProjection;
lastUpdatedAt: string;
}
export interface UserRoiAnalyticsResponse {
userId: string;
period: PeriodInfo;
totalEvents: number;
overallInvestment: InvestmentDetails;
overallRevenue: RevenueDetails;
overallRoi: RoiCalculation;
costEfficiency: CostEfficiency;
projection: RevenueProjection;
eventRois: EventRoiSummary[];
lastUpdatedAt: string;
dataSource: string;
}
export interface EventRoiSummary {
eventId: string;
eventTitle: string;
totalInvestment: number;
expectedRevenue: number;
roi: number;
status: string;
}
export interface ChannelAnalyticsResponse {
eventId: string;
channels: ChannelAnalytics[];
comparison: ChannelComparison;
lastUpdatedAt: string;
}
export interface UserChannelAnalyticsResponse {
userId: string;
period: PeriodInfo;
totalEvents: number;
channels: ChannelAnalytics[];
comparison: ChannelComparison;
lastUpdatedAt: string;
dataSource: string;
}
// ============= API Response Wrapper =============
export interface ApiResponse<T> {
success: boolean;
data: T;
errorCode?: string;
message?: string;
timestamp: string;
}
// ============= API Request Types =============
export interface AnalyticsQueryParams {
startDate?: string;
endDate?: string;
refresh?: boolean;
}
export interface TimelineQueryParams extends AnalyticsQueryParams {
interval?: 'hourly' | 'daily' | 'weekly' | 'monthly';
metrics?: string;
}
export interface ChannelQueryParams extends AnalyticsQueryParams {
channels?: string;
sortBy?: 'views' | 'participants' | 'engagement_rate' | 'conversion_rate' | 'roi';
order?: 'asc' | 'desc';
}
export interface RoiQueryParams {
includeProjection?: boolean;
refresh?: boolean;
}