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:
72
src/utils/formatters.ts
Normal file
72
src/utils/formatters.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* formatters.ts - 공통 포맷팅 유틸리티
|
||||
*
|
||||
* 목적: 트래픽, 대역폭 정보를 일관되게 포맷팅
|
||||
*/
|
||||
|
||||
import type { BandwidthInfo } from '../types';
|
||||
|
||||
/**
|
||||
* 트래픽 값을 적절한 소수점 자리로 포맷팅
|
||||
*
|
||||
* @param value - TB 단위 트래픽 값
|
||||
* @returns 포맷팅된 문자열 (예: "1.23TB", "12.3TB")
|
||||
*
|
||||
* 규칙:
|
||||
* - 10TB 미만: 소수점 2자리
|
||||
* - 10TB 이상: 소수점 1자리
|
||||
*/
|
||||
export function formatTB(value: number): string {
|
||||
if (value < 10) {
|
||||
return `${value.toFixed(2)}TB`;
|
||||
}
|
||||
return `${value.toFixed(1)}TB`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 대역폭 정보를 사용자 친화적 문자열로 포맷팅
|
||||
*
|
||||
* @param bandwidth_info - 대역폭 정보 객체
|
||||
* @param currency - 통화 코드 ('KRW' 또는 'USD')
|
||||
* @returns 포맷팅된 트래픽 정보 문자열
|
||||
*
|
||||
* 포맷:
|
||||
* - CDN 있음 + 초과: "예상 트래픽: 5.00TB (CDN 85% → 원본 0.75TB) → 초과 0.25TB (₩5,000)"
|
||||
* - CDN 있음 + 포함: "예상 트래픽: 5.00TB (CDN 85% → 원본 0.75TB)"
|
||||
* - CDN 없음 + 초과: "예상 트래픽: 1.50TB → 초과 0.50TB (₩10,000)"
|
||||
* - CDN 없음 + 포함: "예상 트래픽: 0.80TB (포함 범위 내)"
|
||||
*/
|
||||
export function formatTrafficInfo(bandwidth_info: BandwidthInfo, currency: string): string {
|
||||
// CDN 정보가 있는 경우
|
||||
if (bandwidth_info.gross_monthly_tb !== undefined && bandwidth_info.cdn_cache_hit_rate !== undefined) {
|
||||
// CDN 캐시 히트율 범위 검증 (0-100%)
|
||||
const hitRate = Math.round(Math.min(1, Math.max(0, bandwidth_info.cdn_cache_hit_rate)) * 100);
|
||||
const grossTB = formatTB(bandwidth_info.gross_monthly_tb);
|
||||
const estimatedTB = formatTB(bandwidth_info.estimated_monthly_tb);
|
||||
|
||||
// 초과 여부 판단 (통일된 조건)
|
||||
if (bandwidth_info.estimated_overage_tb > 0 && bandwidth_info.estimated_overage_cost > 0) {
|
||||
const overageTB = formatTB(bandwidth_info.estimated_overage_tb);
|
||||
const overageCost = currency === 'KRW'
|
||||
? `₩${Math.round(bandwidth_info.estimated_overage_cost).toLocaleString()}`
|
||||
: `$${bandwidth_info.estimated_overage_cost.toFixed(2)}`;
|
||||
return `예상 트래픽: ${grossTB} (CDN ${hitRate}% → 원본 ${estimatedTB}) → 초과 ${overageTB} (${overageCost})`;
|
||||
} else {
|
||||
return `예상 트래픽: ${grossTB} (CDN ${hitRate}% → 원본 ${estimatedTB})`;
|
||||
}
|
||||
} else {
|
||||
// CDN 정보 없는 경우
|
||||
const estimatedTB = formatTB(bandwidth_info.estimated_monthly_tb);
|
||||
|
||||
// 초과 여부 판단 (통일된 조건)
|
||||
if (bandwidth_info.estimated_overage_tb > 0 && bandwidth_info.estimated_overage_cost > 0) {
|
||||
const overageTB = formatTB(bandwidth_info.estimated_overage_tb);
|
||||
const overageCost = currency === 'KRW'
|
||||
? `₩${Math.round(bandwidth_info.estimated_overage_cost).toLocaleString()}`
|
||||
: `$${bandwidth_info.estimated_overage_cost.toFixed(2)}`;
|
||||
return `예상 트래픽: ${estimatedTB} → 초과 ${overageTB} (${overageCost})`;
|
||||
} else {
|
||||
return `예상 트래픽: ${estimatedTB} (포함 범위 내)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user