refactor: extract common bandwidth formatting utilities

- Add BandwidthInfo interface to types.ts (single source of truth)
- Create formatters.ts with formatTB() and formatTrafficInfo()
- Display CDN hit rate and gross/origin traffic in recommendations
- Fix floating point formatting (consistent decimal places)
- Fix undefined handling in toLocaleString() calls
- Unify overage detection logic (overage_tb > 0 && cost > 0)
- Add CDN hit rate range validation (0-100%)
- Extract CDN_CACHE_HIT_RATES constants

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-27 12:33:58 +09:00
parent 0277f0539b
commit 6392a17d4f
4 changed files with 152 additions and 40 deletions

View File

@@ -1,10 +1,20 @@
import type { Env } from '../types';
import type { Env, BandwidthInfo } from '../types';
import { retryWithBackoff, RetryError } from '../utils/retry';
import { createLogger, maskUserId } from '../utils/logger';
import { ERROR_MESSAGES } from '../constants/messages';
import { formatTrafficInfo } from '../utils/formatters';
const logger = createLogger('server-tool');
// CDN 캐시 히트율 상수
const CDN_CACHE_HIT_RATES = {
VIDEO_STREAMING: 0.92,
STATIC_SITE: 0.95,
API: 0.30,
ECOMMERCE: 0.70,
DEFAULT: 0.85,
} as const;
// 언어 감지 (한글/일본어/중국어/영어)
function detectLanguage(text: string): 'ko' | 'ja' | 'zh' | 'en' {
if (/[가-힣]/.test(text)) return 'ko';
@@ -32,12 +42,12 @@ function estimateCdnCacheHitRate(techStack: string[], useCase: string): number |
const isEcommerce = /shop|store|commerce|쇼핑|이커머스/.test(useCaseLower);
// 콘텐츠 타입별 예상 캐시 히트율
if (isVideoStreaming) return 0.92; // 비디오: 92% (대부분 캐시 가능)
if (isStaticSite) return 0.95; // 정적 사이트: 95%
if (isApi) return 0.30; // API: 30% (동적 콘텐츠 많음)
if (isEcommerce) return 0.70; // 이커머스: 70% (상품 이미지 캐시)
if (isVideoStreaming) return CDN_CACHE_HIT_RATES.VIDEO_STREAMING;
if (isStaticSite) return CDN_CACHE_HIT_RATES.STATIC_SITE;
if (isApi) return CDN_CACHE_HIT_RATES.API;
if (isEcommerce) return CDN_CACHE_HIT_RATES.ECOMMERCE;
return 0.85; // 기본값: 85%
return CDN_CACHE_HIT_RATES.DEFAULT;
}
// Type guards
@@ -94,16 +104,7 @@ interface ServerRecommendation {
max_concurrent_users: number;
requests_per_second: number;
};
bandwidth_info?: {
included_transfer_tb: number;
overage_cost_per_gb: number;
overage_cost_per_tb: number;
estimated_monthly_tb: number;
estimated_overage_tb: number;
estimated_overage_cost: number;
total_estimated_cost: number;
currency: string;
};
bandwidth_info?: BandwidthInfo;
benchmark_reference?: {
processor_name: string;
benchmarks: BenchmarkItem[];
@@ -334,20 +335,15 @@ function formatRecommendations(data: RecommendResponse): string {
// 대역폭 정보 (항상 표시)
if (rec.bandwidth_info) {
const bw = rec.bandwidth_info;
if (bw.estimated_overage_cost > 0) {
// 초과 비용 있음
const overageCost = bw.currency === 'KRW'
? `${Math.round(bw.estimated_overage_cost).toLocaleString()}`
: `$${bw.estimated_overage_cost.toFixed(2)}`;
const totalCost = bw.currency === 'KRW'
? `${Math.round(bw.total_estimated_cost).toLocaleString()}`
: `$${bw.total_estimated_cost.toFixed(2)}`;
response += ` • 예상 트래픽: ${bw.estimated_monthly_tb}TB → 초과 ${bw.estimated_overage_tb}TB (${overageCost})\n`;
const trafficInfo = formatTrafficInfo(rec.bandwidth_info, server.currency);
response += `${trafficInfo}\n`;
// 총 예상 비용 (초과 있을 때만 표시)
if (rec.bandwidth_info.estimated_overage_tb > 0 && rec.bandwidth_info.estimated_overage_cost > 0) {
const totalCost = server.currency === 'KRW'
? `${Math.round(rec.bandwidth_info.total_estimated_cost).toLocaleString()}`
: `$${rec.bandwidth_info.total_estimated_cost.toFixed(2)}`;
response += ` • 총 예상 비용: ${totalCost}/월\n`;
} else {
// 포함 범위 내
response += ` • 예상 트래픽: ${bw.estimated_monthly_tb}TB (포함 범위 내)\n`;
}
}