‘Privacy-First’ 환상: 왜 당신의 Analytics Hash는 여전히 Fingerprinting을 하는가

발행: (2026년 4월 17일 AM 04:12 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

“프라이버시 우선” 미라지 표지 이미지: 왜 당신의 분석 해시가 여전히 지문을 남기는가

“프라이버시‑우선”은 모든 새로운 분석 도구의 가장 인기 있는 마케팅 유행어가 되었습니다. 하지만 개발자로서 우리는 랜딩 페이지를 믿어서는 안 되고, 구현을 믿어야 합니다.

저는 Zenovay를 만들고 있습니다. GA4 악몽에 대한 간결한 대안이며, “프라이버시‑친화적” 도구들이 실제로 방문자 신원을 어떻게 처리하는지 감사를 많이 진행했습니다. 그 결과, 프라이버시인 척하는 “경량 지문 채취”가 많이 발견되었습니다.

함정: 사용자 에이전트 지속성

대부분의 인디 분석 도구는 IP를 저장하지 않고 고유 방문자를 식별하기 위해 해시를 사용합니다. 이들의 백엔드에서는 다음과 같은 로직을 자주 볼 수 있습니다(이를 “GhostlyX” 접근법이라고 부릅시다).

// The problematic approach
$visitorHash = hash_hmac('sha256', $ip . $ua . $site->id . $today, config('app.key'));

문제는 $ua(User Agent)입니다. 해시에 사용자 에이전트를 포함하면 사용자의 기기를 지문화하게 되는 것입니다.

사용자가 집 Wi‑Fi에서 5G 네트워크로 이동하면 IP는 바뀌지만 사용자 에이전트는 동일하게 유지됩니다. 도구는 여전히 해당 세션들을 연결할 수 있습니다. 이는 네트워크 변화에도 살아남는 지속적인 식별자를 만들어 내며, 바로 “프라이버시‑우선”이 방지하려는 상황입니다.

Source:

더 깔끔한 접근법: 일일 회전 솔트

Zenovay의 트래킹 엔진을 구축할 때 우리는 이렇게 결정했습니다: 데이터가 익명이어야 한다면 실제로 익명이어야 한다. 지문을 남기지 않는다. 24시간을 초과하는 지속성을 허용하지 않는다.

우리는 Web Crypto API(Cloudflare Workers와 같은 엣지 환경에서 기본 제공)를 사용해 IP가 서버에 도착하는 순간 바로 처리합니다.

구현

async function hashIPForVisitor(ip: string, websiteId: string): Promise<string> {
    // Daily salt ensures no long‑term tracking
    const today = new Date().toISOString().slice(0, 10); // YYYY‑MM‑DD

    const encoder = new TextEncoder();
    // We combine IP, Website ID, and the daily salt.
    // NOTICE: No User Agent. No device fingerprinting.
    const data = encoder.encode(`${ip}|${websiteId}|${today}`);

    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));

    return hashArray
        .map(b => b.toString(16).padStart(2, '0'))
        .join('');
}

왜 이것이 기술적으로 우수한가

  • 영구 저장 없음${today}를 포함하기 때문에 같은 방문자의 해시가 자정에 바뀝니다. 여러 날에 걸쳐 사용자를 “추적”할 방법이 없습니다.
  • 지문 방지 – User Agent를 제외함으로써 “특정 시간에 발생한 연결”만 측정하고, “특정 기기”는 측정하지 않습니다.
  • 데이터베이스 무결성 – 우리 Supabase 스키마에서 ip_addressnull로 고정되어 있습니다. 원본 IP는 디스크에 전혀 기록되지 않습니다.
const visitorHash = await hashIPForVisitor(clientIP, website.id);

await supabase.from('hits').insert({
    website_id: website.id,
    ip_hash: visitorHash,
    ip_address: null, // Raw IP is never stored
    pathname: request.url.pathname,
    // ...
});
```}

## 아키텍처: Cloudflare + Supabase + Next.js

현대적인 엣지 스택을 기반으로 구축함으로써 전체 추적 스크립트를 **1 KB** 이하로 유지할 수 있었습니다.

- **Cloudflare Workers**는 들어오는 요청을 처리하고 엣지에서 해시를 수행합니다. 사용자의 네트워크 홉을 떠나는 데이터가 없기 전에 이루어집니다.  
- **Supabase (PostgreSQL)**는 해시된 이벤트를 저장합니다. 불필요하게 부풀린 추적 데이터가 없으므로 수백만 행에서도 쿼리가 빠르게 유지됩니다.  
- **Next.js (App Router)**는 대시보드에 동력을 제공하며, 히트를 실시간 Stripe 수익 귀속과 연결합니다.

## 프라이버시 마케팅 vs. 프라이버시 엔지니어링

차이는 투명성에 달려 있습니다. 도구가 방문자 ID를 어떻게 생성하는지 정확히 알려주지 않는다면, 아마도 당신을 지문 인식하고 있는 것입니다.

진정한 프라이버시는 데이터를 난독화하는 것이 아니라, 민감한 데이터가 **데이터베이스에 도달하기 전에 사라지도록** 하는 것입니다.

저는 Zenovay를 부트스트랩하여 사용자를 제품으로 전환하지 않고도 세계 수준의 비즈니스 인사이트와 수익 귀속을 얻을 수 있음을 증명하고 있습니다.

*트래킹 스택에서 PII를 어떻게 처리하고 있나요? Web Crypto API를 사용하고 있나요, 아니면 전통적인 서버‑사이드 해싱을 고수하고 있나요? 댓글에 알려 주세요.*
0 조회
Back to Blog

관련 글

더 보기 »