chore(lps): 임시 FE — 검색 원가 4타일(소요/AI비용/토큰/크롤 트래픽)

작업 카드에 result.metrics 렌더: 소요시간(+소스별), AI 비용$·호출수, AI 토큰(in/out),
크롤 트래픽(html bytes·fetch수·크롤 몰).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
민헌 2026-07-09 15:17:42 +09:00
parent 69f6641c27
commit 9c478e80fd

View File

@ -135,6 +135,15 @@
.sec-title{font-size:12px;font-weight:700;color:var(--text-700);margin:0 0 9px;display:flex;align-items:center;gap:7px}
.sec-title .cnt{font-size:11px;color:var(--text-400);font-weight:600}
/* 검색 원가 계측 */
.metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:16px}
@media (max-width:520px){.metrics{grid-template-columns:repeat(2,1fr)}}
.metric{border:1px solid var(--border-2);border-radius:var(--radius-sm);padding:10px 11px;background:var(--surface-2)}
.metric .mk{font-size:10.5px;font-weight:700;color:var(--text-400);text-transform:uppercase;letter-spacing:.03em;margin-bottom:4px}
.metric .mv{font-size:16px;font-weight:800;font-variant-numeric:tabular-nums;line-height:1.1}
.metric .mv small{font-size:11px;font-weight:600;color:var(--text-500)}
.metric .msub{font-size:10.5px;color:var(--text-400);font-weight:600;margin-top:2px}
table{width:100%;border-collapse:collapse;font-size:13px}
th{text-align:left;font-size:11px;font-weight:700;color:var(--text-500);text-transform:uppercase;letter-spacing:.03em;padding:8px 10px;border-bottom:1px solid var(--border-2)}
td{padding:9px 10px;border-bottom:1px solid var(--border-2);vertical-align:top}
@ -499,6 +508,23 @@ function renderJob(d, submitted){
html += `</div>`;
}
// 검색 원가 계측(리소스/비용/시간)
const mt=o.metrics;
if(mt){
const kb=(mt.crawl?.html_bytes||0)/1024;
const kbTxt = kb>=1024 ? (kb/1024).toFixed(1)+" MB" : Math.round(kb)+" KB";
const cost=mt.ai?.est_cost_usd||0;
const srcMs=mt.source_ms||{};
const srcTxt=Object.entries(srcMs).map(([s,ms])=>`${(SRC[s]||{}).label||s} ${(ms/1000).toFixed(1)}s`).join(" · ");
html += `<div class="sec-title">검색 원가 <span class="cnt">이 검색 1건이 쓴 리소스·비용·시간</span></div>
<div class="metrics">
<div class="metric"><div class="mk">소요 시간</div><div class="mv">${(mt.duration_ms/1000).toFixed(1)}<small>s</small></div>${srcTxt?`<div class="msub" title="${esc(srcTxt)}">${esc(srcTxt.length>34?srcTxt.slice(0,34)+'…':srcTxt)}</div>`:``}</div>
<div class="metric"><div class="mk">AI 비용</div><div class="mv">$${cost.toFixed(cost<0.01?6:4)}</div><div class="msub">${mt.ai?.calls||0}회 호출</div></div>
<div class="metric"><div class="mk">AI 토큰</div><div class="mv">${won((mt.ai?.prompt_tokens||0)+(mt.ai?.completion_tokens||0))}</div><div class="msub">in ${won(mt.ai?.prompt_tokens||0)} / out ${won(mt.ai?.completion_tokens||0)}</div></div>
<div class="metric"><div class="mk">크롤 트래픽</div><div class="mv">${kbTxt}</div><div class="msub">${mt.crawl?.fetches||0} fetch${(mt.crawl?.malls_crawled||[]).length?' · 크롤 '+mt.crawl.malls_crawled.map(s=>(SRC[s]||{}).label||s).join(','):''}</div></div>
</div>`;
}
// 몰별 최저가 분해 (네이버 payload에 이미 들어온 G마켓/옥션/11번가 등을 추가요청 0으로 노출)
const byMall=o.by_mall||[];
if(byMall.length>1){