사용자가 어느 화면에서 이탈하는지 보려면 이벤트 추적 도구가 필요하고, 새 기능을 일부 사용자에게만 먼저 열어보려면 피처 플래그가 필요하고, "그 버튼을 왜 안 눌렀는지" 직접 눈으로 보려면 세션 리플레이가 필요합니다. 보통 이 세 가지는 각자 다른 회사 제품입니다 — Mixpanel이나 Amplitude로 이벤트를 보고, LaunchDarkly로 플래그를 관리하고, Hotjar나 FullStory로 화면을 녹화하는 식입니다. PostHog는 이 세 가지(와 A/B 테스트, 설문까지)를 한 제품, 한 SDK로 묶었습니다.
핵심 개념
- 분석·리플레이·플래그가 같은
distinct_id를 공유합니다. 세션 리플레이 영상을 보다가 "이 사용자가 어떤 이벤트를 발생시켰는지" 바로 옆 패널에서 확인할 수 있는 이유가 이것입니다. 다른 조합(Mixpanel + Hotjar)에서는 두 서비스의 사용자 식별자가 따로 관리돼서 이런 연결이 안 됩니다. - 익명 이벤트와 식별된 이벤트가 분리됩니다.
posthog.init()만 호출하면 로그인 전부터 브라우저에 저장된 임의의distinct_id로 이벤트가 쌓이고, 로그인 시점에posthog.identify(userId)를 호출하면 그 전까지의 익명 이벤트가 실제 사용자로 병합됩니다. 이 호출을 빠뜨리면 같은 사람의 가입 전·후 행동이 서로 다른 사용자로 집계됩니다. - 피처 플래그는 로컬 평가(local evaluation)를 지원합니다. 서버 SDK에 플래그 정의를 캐시해두면 요청마다 PostHog 서버에 네트워크 호출을 하지 않고 로컬에서 바로 true/false를 판정할 수 있어서, 트래픽이 많은 API 라우트에 플래그를 걸어도 지연이 거의 안 붙습니다.
- 오픈소스라 셀프호스팅도 가능합니다. 코어 저장소가 공개돼 있어 자체 서버에 Docker Compose로 올릴 수 있고, 클라우드와 셀프호스팅 모두 같은 SDK를 씁니다. PostHog 자체 집계로는 전체 고객의 90% 이상이 무료 티어 안에서만 쓰고 있다고 밝히고 있습니다.
실전 예시
클라이언트 쪽은 posthog-js 하나로 시작합니다.
npm install posthog-js
// app/providers.tsx (Next.js App Router)
"use client";
import posthog from "posthog-js";
import { useEffect } from "react";
export function PostHogProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: "https://us.i.posthog.com",
person_profiles: "identified_only",
});
}, []);
return <>{children}</>;
}
로그인 성공 시점과 커스텀 이벤트는 이렇게 보냅니다.
// 로그인 성공 콜백
posthog.identify(user.id, { email: user.email });
// 커스텀 이벤트
posthog.capture("project_created", { plan: "free" });
// 피처 플래그 분기
if (posthog.isFeatureEnabled("new-onboarding-flow")) {
// 새 온보딩 화면 렌더링
}
서버 쪽에서 이벤트를 보내야 할 때(결제 webhook 처리 등)는 posthog-node를 씁니다.
npm install posthog-node
// app/api/webhook/route.ts
import { PostHog } from "posthog-node";
const client = new PostHog(process.env.POSTHOG_KEY!, {
host: "https://us.i.posthog.com",
});
export async function POST(req: Request) {
const event = await req.json();
client.capture({
distinctId: event.userId,
event: "subscription_upgraded",
properties: { plan: event.plan },
});
await client.shutdown();
return Response.json({ ok: true });
}
서버리스 함수에서 client.shutdown()을 빼먹으면 이벤트가 안 보이는 경우가 있습니다. posthog-node는 이벤트를 모아서 배치로 전송하는데, 함수가 응답을 반환하고 바로 freeze되면 아직 큐에 쌓여 있던 이벤트가 전송되기 전에 날아가 버립니다. shutdown()을 await해서 남은 큐를 강제로 flush한 다음 응답을 보내야 누락이 안 생깁니다.
세션 리플레이를 켜고 처음 돌려봤을 때는 로그인 폼의 입력 필드가 전부 까만 박스로 가려져 있었습니다. 기본 설정(maskAllInputs)이 모든 입력값을 마스킹하기 때문인데, 비밀번호 입력창 하나 때문에 전체 입력 필드가 가려지는 거라 디버깅용으로 특정 필드만 풀어주려면 session_recording 옵션에서 마스킹 대상을 따로 지정해야 합니다.
비슷한 서비스와 비교
| 항목 | PostHog | Mixpanel | Amplitude |
|---|---|---|---|
| 과금 기준 | 이벤트 수 | MTU(월간 추적 사용자) | MTU |
| 세션 리플레이 | 내장 | 없음 | 내장 |
| 피처 플래그 | 내장(로컬 평가 지원) | 제한적 | 내장 |
| A/B 테스트 | 내장 | 제한적 | 내장 |
| 오픈소스/셀프호스팅 | 가능 | 불가 | 불가 |
가장 큰 차이는 과금 기준입니다. MTU 기준 과금은 이벤트 양이 똑같아도 활성 사용자 수가 늘면 그대로 비용이 뛰는 구조라, 사용자가 급격히 늘어나는 초기 스타트업에서 체감 비용이 빠르게 올라가는 편입니다. PostHog는 이벤트 수 기준이라 사용자 수가 늘어도 한 사용자당 보내는 이벤트 양이 그대로면 비용이 비례해서 뛰지 않습니다. 다만 Mixpanel·Amplitude는 특정 업종(마케팅 퍼널 분석 등)에 맞춘 리포트 템플릿이 더 다듬어져 있다는 평가가 많아서, 분석 리포트 자체의 완성도만 보면 아직 PostHog가 따라가는 영역도 있습니다.
비용
무료 티어는 매달 제품 분석 이벤트 100만 건, 세션 리플레이 5,000회, 피처 플래그 요청 100만 건까지입니다. 이 한도를 넘으면 상품별로 따로 종량제가 붙는데, 이벤트 기준으로는 100만200만 건 구간이 건당 $0.00005, 200만1,500만 건 구간은 건당 $0.0000343으로 내려가고, 물량이 커질수록(1,500만5,000만: $0.0000295, 5,000만1억: $0.0000218, 1억~2.5억: $0.0000150, 2.5억 건 초과: $0.0000090) 단가가 계속 떨어지는 구조입니다. 세션 리플레이·피처 플래그도 각각 별도 무료 한도와 종량제를 갖고 있어서, 세 기능을 전부 쓴다고 비용이 단순히 세 배가 되는 건 아닙니다.
사이드 프로젝트나 초기 단계 서비스라면 월 100만 이벤트 안에서 분석·리플레이·플래그를 전부 무료로 쓸 수 있는 범위가 꽤 넉넉합니다. 반대로 이미 Mixpanel이나 Amplitude를 쓰고 있고 세션 리플레이·피처 플래그가 필요 없다면, 굳이 도구를 갈아탈 이유까지는 아닙니다.