9148c358d0
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
6.5 KiB
6.5 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | tags | raw_sources | last_reinforced | github_commit | tech_stack | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| wiki-2026-0508-datacollector-knowledge-hub | Datacollector Knowledge Hub | 10_Wiki/Topics | verified | self |
|
none | A | 0.85 | applied |
|
2026-05-10 | pending |
|
Datacollector Knowledge Hub
매 한 줄
"매 datacollector = browser event → edge ingest → warehouse 의 매 reliable pipeline.". 매 frontend datacollector는 매 user behavior, performance (Web Vitals), error 의 capture → 매 batch / sendBeacon → edge function (Cloudflare Workers / Vercel) → 매 ClickHouse / BigQuery / Snowflake. 매 2026 perspective는 매 1st-party domain ingest + GDPR/ePrivacy 동의 + server-side GTM.
매 핵심
매 collector responsibilities
- Capture: page view, click, custom event, performance, error.
- Enrich: session id, user id (consent-gated), referrer, UTM.
- Batch & Buffer: idle batching, sendBeacon on
pagehide. - Privacy: consent state, IP truncation, PII scrubbing.
- Transport: 1st-party endpoint > 3rd-party (avoid adblock).
매 Web Vitals
- LCP, INP, CLS, TTFB, FCP —
web-vitalslibrary. - 매 attribution build (
onINP({ reportAllChanges: true })) 매 root-cause.
매 응용
- Product analytics (PostHog, Amplitude, Mixpanel, Segment).
- RUM (Datadog, Sentry, New Relic, SpeedCurve).
- A/B testing exposure logging.
- Funnel & cohort analysis.
💻 패턴
1. Minimal collector (sendBeacon + queue)
type Event = { type: string; ts: number; props?: Record<string, unknown> };
const queue: Event[] = [];
const ENDPOINT = '/collect'; // 1st-party
function track(type: string, props?: Event['props']) {
queue.push({ type, ts: Date.now(), props });
if (queue.length >= 20) flush();
}
function flush() {
if (!queue.length) return;
const batch = queue.splice(0, queue.length);
const blob = new Blob([JSON.stringify(batch)], { type: 'application/json' });
navigator.sendBeacon(ENDPOINT, blob) || fetch(ENDPOINT, { method: 'POST', body: blob, keepalive: true });
}
addEventListener('pagehide', flush);
addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') flush(); });
2. Web Vitals capture
import { onLCP, onINP, onCLS, onTTFB } from 'web-vitals';
const send = (metric: { name: string; value: number; id: string }) =>
track('vital', { name: metric.name, value: metric.value, id: metric.id });
onLCP(send); onINP(send); onCLS(send); onTTFB(send);
3. Error capture (global)
window.addEventListener('error', (e) => {
track('error', { msg: e.message, src: e.filename, line: e.lineno, col: e.colno, stack: e.error?.stack });
});
window.addEventListener('unhandledrejection', (e) => {
track('promise_rejection', { reason: String(e.reason) });
});
4. Auto click tracking (delegated)
document.addEventListener('click', (e) => {
const t = (e.target as Element).closest('[data-track]');
if (t instanceof HTMLElement) {
track('click', { id: t.dataset.track, ...t.dataset });
}
});
5. Consent gating (TCFv2)
function hasAnalyticsConsent(): boolean {
const tcData = (window as any).__tcfapi;
// simplified — production: subscribe to TCF api
return localStorage.getItem('consent.analytics') === '1';
}
const origTrack = track;
const trackGated = (type: string, props?: Event['props']) => {
if (!hasAnalyticsConsent()) return;
origTrack(type, props);
};
6. Edge ingest (Cloudflare Worker)
// worker.ts
export default {
async fetch(req: Request, env: Env): Promise<Response> {
if (req.method !== 'POST') return new Response('', { status: 405 });
const events = await req.json<Event[]>();
const enriched = events.map((e) => ({
...e,
ip_country: req.cf?.country,
ua: req.headers.get('user-agent'),
ingest_ts: Date.now(),
}));
await env.QUEUE.send(enriched);
return new Response('', { status: 204 });
},
};
7. Server-side GTM forward
// edge → GTM SS container
await fetch('https://gtm.example.com/g/collect', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ events: enriched }),
});
8. PII scrub
const EMAIL = /[\w.+-]+@[\w-]+\.[\w.-]+/g;
const PHONE = /\+?\d[\d \-]{8,}\d/g;
function scrub<T extends Record<string, unknown>>(props: T): T {
return JSON.parse(JSON.stringify(props).replace(EMAIL, '[email]').replace(PHONE, '[phone]'));
}
9. Session identity
const SESSION_KEY = 'sid';
const TIMEOUT_MS = 30 * 60 * 1000;
function getSession() {
const raw = sessionStorage.getItem(SESSION_KEY);
const parsed = raw ? JSON.parse(raw) : null;
if (parsed && Date.now() - parsed.last < TIMEOUT_MS) {
parsed.last = Date.now();
} else {
parsed?.id || (Object.assign({}, { id: crypto.randomUUID(), last: Date.now() }));
}
const out = parsed ?? { id: crypto.randomUUID(), last: Date.now() };
sessionStorage.setItem(SESSION_KEY, JSON.stringify(out));
return out.id;
}
매 결정 기준
| 상황 | Approach |
|---|---|
| 매 product analytics | PostHog (OSS) or Amplitude. |
| 매 RUM perf | Sentry / Datadog RUM. |
| Privacy strict (EU) | 1st-party + consent + IP truncation. |
| Adblock evasion | 매 1st-party domain reverse-proxy. |
| 자체 build | sendBeacon + edge function + ClickHouse. |
기본값: web-vitals + 매 1st-party /collect + sendBeacon batch + edge ingest.
🔗 Graph
- 부모: Observability
- 응용: Sentry
- Adjacent: Web Vitals · GDPR · ClickHouse
🤖 LLM 활용
언제: 매 collector skeleton, consent gating, batch / flush logic. 언제 X: 매 specific vendor SDK 의 detailed config — 매 vendor docs.
❌ 안티패턴
fetchwithoutkeepalive: 매 unload 매 request drop.- No batching: 매 매 event = 매 request — flood.
- PII without consent: 매 GDPR breach.
- 3rd-party domain only: 매 adblock 차단 — 매 1st-party proxy.
- Sync XHR on unload: 매 deprecated — sendBeacon 사용.
🧪 검증 / 중복
- Verified (web.dev web-vitals, IAB TCFv2, MDN sendBeacon).
- 신뢰도 A.
🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — datacollector pipeline + Web Vitals + consent |