고객사 사이트 공통 헤더에 태그 한 줄만 넣으면 됩니다(GTM 과 같은 방식).
서버에 들어가지 않아도, 방문자가 실제로 받은 바이트와 체감 로딩 시간을 잽니다.
왜 브라우저에게 묻나 — 이미지·CSS·JS 는 PHP 를 거치지 않고 웹서버가 바로 내보냅니다.
그래서 서버 쪽에서는 어떤 방법으로도 셀 수 없습니다. 브라우저는 자기가 받은 바이트를 정확히
알고 있고(캐시에서 꺼낸 것은 0), 그 값을 숫자로만 보내옵니다 —
주소·IP·입력값은 보내지 않습니다.
수집 서버에 닿지 않습니다.embed 서비스가 떠 있는지 봅니다.
(이 화면은 embed/api/embed/traffic 를 읽습니다)
1. 태그 주소 정하기
고객사 브라우저가 직접 받아 가는 주소라 이 콘솔 주소와 다릅니다.
콘솔은 비밀번호 뒤에 두지만 태그는 누구나 받을 수 있어야 하기 때문입니다.
그래서 자동으로 알아낼 수 없어 한 번만 적어 두시면 이 브라우저에 기억합니다.
이 주소는 http:// 입니다 — 고객사 사이트가 https 면 태그가 아예 안 뜹니다.
브라우저가 https 페이지에서 http 스크립트를 조용히 차단합니다(mixed content). 오류도 안 나고
숫자만 계속 0 으로 남아 「왜 전송량이 안 쌓이지」 로 헤매게 됩니다. 실제로 그랬습니다.
수집 서버에 https 주소를 붙여서 그 주소를 적으십시오. 검증할 때만 쓰실 거면
고객사 사이트도 http:// 로 열어야 짝이 맞습니다.
2. 사이트별 상태
숫자를 읽을 때 유의할 것
· JS 가 도는 진짜 브라우저만 보고합니다 — 봇이 빠져 호스팅 관리자 화면 숫자보다 작게 나옵니다.
처음 2~3주는 나란히 보며 보정 계수를 잡는 쪽을 권합니다.
· 재방문자가 캐시로 받은 것은 0 바이트로 옵니다. 깎인 것이 아니라 실제로 안 받은 것입니다.
· 사이트가 죽으면 태그도 안 뜹니다 — 다운 감지는 밖에서 찔러 보는 감시(blackbox)가 맡습니다. 둘은 짝입니다.
<' + '/script>';
const done = () => { const o = b.textContent; b.textContent = '복사했습니다'; setTimeout(() => { b.textContent = o; }, 1500); };
// ★ clipboard API 는 https 나 localhost 가 아니면 없다. 그때는 옛 방법으로 떨어진다.
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(txt).then(done, fallback);
} else fallback();
function fallback() {
try {
const ta = document.createElement('textarea');
ta.value = txt; ta.style.position = 'fixed'; ta.style.opacity = '0';
document.body.appendChild(ta); ta.select(); document.execCommand('copy');
document.body.removeChild(ta); done();
} catch (e) { b.textContent = '복사 실패 — 직접 선택하세요'; }
}
});
});
}
/* ── 읽어오기 ──────────────────────────────────────────────────────────── */
async function load() {
try {
const r = await fetch('embed/api/embed/traffic', { cache: 'no-store' });
if (!r.ok) throw new Error('HTTP ' + r.status);
const d = await r.json();
$('down').classList.add('hide');
$('age').textContent = new Date().toLocaleTimeString('ko-KR');
render(d);
} catch (e) {
$('down').classList.remove('hide');
$('age').textContent = '못 읽음';
}
}
// 콘솔 머리말의 환경 이름(다른 화면과 같은 곳에서 읽는다)
fetch('links.json', { cache: 'no-store' })
.then((r) => (r.ok ? r.json() : null))
.then((j) => { if (j && j.env) $('crumb').textContent = j.env.project || j.env.name || '—'; })
.catch(() => {});
load();
setInterval(load, 15000);