
프로덕트 분석: 내 서비스를 누가, 어떻게 쓰는지 모르고 있었다
서비스를 운영하면서 유저가 어디서 이탈하는지, 어떤 기능을 쓰는지 전혀 몰랐다. PostHog를 붙이고 데이터 기반으로 결정하기 시작한 경험.

서비스를 운영하면서 유저가 어디서 이탈하는지, 어떤 기능을 쓰는지 전혀 몰랐다. PostHog를 붙이고 데이터 기반으로 결정하기 시작한 경험.
거창한 포트폴리오보다 작은 사이드 프로젝트가 더 강력한 이유. 6개월간 만든 앱이 망하고, 주말에 만든 도구가 크게 성공한 경험을 통해 배운 '완성'의 중요성을 이야기합니다.

AI 프로젝트를 시작할 때 가장 먼저 부딪히는 문제. 정답이 있는 데이터가 없을 때 어떻게 해야 할까? 지도, 비지도, 준지도 학습의 현실적인 선택 가이드.

개발자에게 기술 블로그는 선택이 아닌 필수입니다. 단순히 배운 것을 기록하는 것을 넘어, 커리어에서의 강력한 무기가 되고, 메타인지 학습법을 실천하는 최고의 도구입니다. 블로그를 시작하고 6개월 만에 일어난 변화와 꾸준히 쓰는 노하우를 공유합니다.

SaaS에 결제를 붙이려고 Stripe을 연동했다. 결제 코드는 다른 코드와 차원이 다른 긴장감이 있었다. Checkout Session부터 Webhook까지 실전 경험.

기능을 열심히 만들었다. 필터 기능, 소셜 로그인, 대시보드 커스터마이징, 알림 설정. 스프린트마다 무언가를 내보냈고, 매번 "이건 유저들이 좋아할 거야"라고 혼자 확신했다.
그러다 어느 날 냉정하게 물어봤다. 유저들이 실제로 뭘 쓰고 있는지 알아? 대답을 못 했다.
사용자 수는 알았다. 가입자가 몇 명인지, 어제 방문자가 몇 명인지. 그게 전부였다. 어떤 기능을 쓰는지, 어디서 막히는지, 가입하고 나서 뭘 처음 하는지, 일주일 뒤에도 돌아오는지 — 하나도 몰랐다.
더 황당한 건 나중에 알게 된 사실이었다. 세 달 동안 공들여 만든 필터 기능의 실제 사용률이 3%도 안 됐다. 반면에 내가 임시 방편으로 대충 만든 CSV 내보내기 기능은 가입자의 40%가 매주 쓰고 있었다. 그 기능이 중요한지 몰라서, 유지보수도 안 하고, UI 개선 계획도 없었다.
눈을 가리고 주방에서 요리를 하는 셈이었다. 냄새도 못 맡고, 맛도 못 보면서 "이 정도면 맛있겠지"라고 혼자 생각하는 것. 결과물이 어떻게 나올지는 운에 맡기는 것이다.
그때부터 프로덕트 애널리틱스(Product Analytics)를 진지하게 파기 시작했다.
처음에 가지고 있던 숫자들은 전부 허영 지표(Vanity Metrics)였다. 총 가입자 수, 페이지뷰, DAU. 보기에는 그럴듯하고, 투자자한테 보여주기엔 괜찮지만, 실제 의사결정에는 쓸모가 없다.
허영 지표의 문제는 "뭘 해야 하는지"를 알려주지 않는다는 것이다. 가입자가 천 명이라는 사실은 알겠는데, 그래서 뭘 고쳐야 하지? 어디에 시간을 써야 하지? 답이 없다.
반면 내가 진짜 알아야 했던 건 행동 지표(Behavioral Metrics)였다.
| 허영 지표 | 행동 지표 |
|---|---|
| 총 가입자 수 | 가입 후 7일 리텐션율 |
| 페이지뷰 | 핵심 기능 사용률 |
| 세션 수 | 퍼널 완료율 |
| DAU | 활성화율 (Activation Rate) |
| 평균 세션 길이 | 피처별 도달률 |
행동 지표는 "유저가 무엇을 했는가"에서 시작한다. 가입한 유저가 핵심 기능을 써봤는가(활성화). 써본 유저가 일주일 뒤에도 돌아왔는가(리텐션). 어떤 단계에서 빠져나갔는가(퍼널 이탈). 이 숫자들이 있어야 무엇을 개선해야 할지 보인다.
비유하자면 허영 지표는 체중계 숫자와 같다. "현재 상태"는 알려주지만, 몸의 어느 부분이 문제인지, 뭘 먹어야 하는지는 알려주지 않는다. 행동 지표는 MRI 결과지다. 어디가 약한지, 어디에 집중해야 하는지 구체적으로 보인다.
여러 툴을 비교했다. Mixpanel, Amplitude, Heap, Google Analytics. 최종적으로 PostHog를 택했다. 이유는 단순했다.
오픈소스 + 셀프호스팅 가능. 데이터를 외부로 보내지 않아도 된다. B2B 서비스라면 고객 데이터를 제3자에게 넘기는 것 자체가 계약 위반일 수 있다. PostHog Cloud를 써도 되고, 직접 서버에 올려도 된다.
기능이 하나로 묶여 있다. 이벤트 트래킹, 퍼널 분석, 세션 리플레이, 피처 플래그, A/B 테스트까지 전부 PostHog 하나로 된다. Mixpanel로 이벤트 트래킹하고, LaunchDarkly로 피처 플래그 관리하고, FullStory로 세션 리플레이 보고... 이렇게 쪼개면 비용도 비용이지만, 도구 간에 컨텍스트가 끊긴다.
무료 한도가 넉넉하다. PostHog Cloud 기준으로 월 100만 이벤트까지 무료다. 초기 서비스라면 충분하다.
비교표로 정리해보면 이렇다.
| PostHog | Mixpanel | Amplitude | Google Analytics | |
|---|---|---|---|---|
| 가격 | 월 1M 이벤트 무료 | 월 10만 이벤트 무료 | 월 10만 이벤트 무료 | 무료 (GA4) |
| 셀프호스팅 | ✅ 가능 | ❌ 불가 | ❌ 불가 | ❌ 불가 |
| 오픈소스 | ✅ MIT | ❌ | ❌ | ❌ |
| 세션 리플레이 | ✅ 내장 | ❌ 별도 | ❌ 별도 | 제한적 |
| 피처 플래그 | ✅ 내장 | ❌ 별도 | ❌ 별도 | ❌ |
| A/B 테스트 | ✅ 내장 | 제한적 | ✅ | ❌ |
| 학습 곡선 | 중간 | 낮음 | 중간 | 낮음 |
| B2B 적합성 | ✅ 높음 | 보통 | 보통 | 낮음 |
Google Analytics는 여전히 유용하다. 하지만 프로덕트 분석 용도로는 설계가 다르다. GA는 웹 트래픽과 마케팅 캠페인 성과를 보는 도구에 가깝다. 유저가 앱 안에서 어떤 행동을 했는지, 어디서 막혔는지를 분석하려면 이벤트 기반 툴이 맞다.
설치는 간단하다.
npm install posthog-js
Next.js App Router 환경이라면 Provider 컴포넌트를 하나 만들어서 루트에 넣는다.
// src/components/PostHogProvider.tsx
"use client";
import posthog from "posthog-js";
import { PostHogProvider as PHProvider } from "posthog-js/react";
import { useEffect } from "react";
export function PostHogProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST ?? "https://app.posthog.com",
capture_pageview: false, // 페이지뷰는 수동으로 처리
capture_pageleave: true,
session_recording: {
maskAllInputs: true, // 입력값은 마스킹 (비밀번호, 개인정보)
},
});
}, []);
return <PHProvider client={posthog}>{children}</PHProvider>;
}
// src/app/layout.tsx
import { PostHogProvider } from "@/components/PostHogProvider";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<PostHogProvider>
{children}
</PostHogProvider>
</body>
</html>
);
}
Next.js App Router는 페이지 전환이 클라이언트 사이드에서 일어나기 때문에, 페이지뷰 트래킹을 별도로 처리해야 한다. capture_pageview: false로 끄고, 라우터 이벤트에 맞춰 수동으로 캡처하는 게 정확하다.
// src/components/PostHogPageView.tsx
"use client";
import { usePathname, useSearchParams } from "next/navigation";
import { usePostHog } from "posthog-js/react";
import { useEffect } from "react";
export function PostHogPageView() {
const pathname = usePathname();
const searchParams = useSearchParams();
const posthog = usePostHog();
useEffect(() => {
if (pathname && posthog) {
let url = window.origin + pathname;
if (searchParams.toString()) {
url += `?${searchParams.toString()}`;
}
posthog.capture("$pageview", { $current_url: url });
}
}, [pathname, searchParams, posthog]);
return null;
}
페이지뷰만으로는 부족하다. 유저가 어떤 버튼을 눌렀는지, 어떤 기능을 완료했는지를 직접 이벤트로 찍어야 한다.
// 이벤트 트래킹 예시
import { usePostHog } from "posthog-js/react";
function ExportButton({ reportId }: { reportId: string }) {
const posthog = usePostHog();
const handleExport = async () => {
// 이벤트 캡처: 이벤트명 + 프로퍼티
posthog.capture("report_exported", {
report_id: reportId,
format: "csv",
source: "dashboard",
});
await exportReport(reportId);
};
return <button onClick={handleExport}>CSV로 내보내기</button>;
}
이벤트 이름을 짓는 규칙이 중요하다. 나중에 수백 개의 이벤트가 쌓이면 이름이 엉망이면 찾기가 힘들어진다. 내가 쓰는 컨벤션은 <명사>_<동사> 패턴이다. button_clicked가 아니라 report_exported, user_invited, filter_applied처럼 구체적으로.
유저 식별도 핵심이다. 익명 유저로 쌓인 이벤트와 로그인 이후의 이벤트를 연결해야 퍼널이 정확해진다.
// 로그인 성공 후 유저 식별
import posthog from "posthog-js";
async function onLoginSuccess(user: User) {
// identify: 익명 세션 → 실제 유저 연결
posthog.identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan, // 'free' | 'pro' | 'enterprise'
created_at: user.createdAt,
company_name: user.company, // B2B라면 회사 단위 분석도 가능
});
// 그룹 분석: 개인이 아닌 팀/조직 단위로 볼 때
if (user.organizationId) {
posthog.group("organization", user.organizationId, {
name: user.organizationName,
plan: user.organizationPlan,
});
}
}
// 로그아웃 시 반드시 reset
function onLogout() {
posthog.reset();
}
posthog.reset()을 로그아웃 시 호출하지 않으면, 다음에 다른 사람이 같은 기기에서 로그인했을 때 이전 유저의 세션이 이어진다. 공용 컴퓨터나 공유 계정 환경에서 데이터가 섞이는 원인이 된다.
PostHog를 붙이고 나서 처음엔 모든 이벤트를 다 보려다가 오히려 혼란스러웠다. 지표가 너무 많으면 없는 것과 같다. 내가 실제로 의사결정에 쓰는 지표만 추려봤다.
신규 가입 유저 중 "아, 이 서비스 쓸 만하다"는 순간을 경험한 비율. 이걸 Aha Moment라고 부른다. 서비스마다 다른데, 나는 "가입 후 48시간 이내에 첫 번째 리포트를 만든 유저"를 활성화 기준으로 잡았다.
활성화율이 낮으면 온보딩이 문제다. 기능이 없는 게 아니라, 유저가 핵심 가치를 경험하기 전에 떠나고 있다는 신호다.
가입 후 7일, 30일에 다시 돌아온 비율. 이게 서비스의 생명력을 보여주는 숫자다. 광고를 아무리 때려도 리텐션이 낮으면 밑 빠진 독에 물 붓기다.
PostHog의 리텐션 차트는 코호트(가입 시점 기준 그룹) 단위로 볼 수 있다. 6월에 가입한 유저들이 8월에도 쓰고 있는지, 그 비율이 5월 코호트보다 나아졌는지를 비교할 수 있다. 제품 개선의 효과가 보인다.
"가입 → 이메일 인증 → 첫 번째 프로젝트 생성 → 팀원 초대 → 유료 전환" 같은 흐름에서 각 단계의 통과율을 보는 것이다. 어느 단계에서 가장 많이 빠지는지 보이면, 거기에 집중해서 개선한다.
내 서비스의 경우 "이메일 인증 → 첫 번째 프로젝트 생성" 단계에서 60%가 이탈하고 있었다. 이메일 인증 후에 빈 화면이 뜨고, 뭘 해야 할지 안내가 없어서였다. 템플릿을 보여주는 것 하나로 이탈률이 35%로 줄었다.
특정 기능을 한 번이라도 써본 활성 유저의 비율. 이게 3%라면, 그 기능은 97%의 유저에게 존재하지 않는 것과 같다. 세 달 공들여 만든 내 필터 기능이 그랬다.
도달률이 낮으면 두 가지 중 하나다. 기능이 필요 없거나, 기능이 있는 줄 모르거나. 세션 리플레이를 같이 보면 구분이 된다.
숫자는 "무엇"이 일어났는지 알려준다. 세션 리플레이는 "왜"를 보여준다.
특정 단계에서 이탈률이 높다는 건 데이터로 알 수 있다. 하지만 유저가 그 화면에서 뭘 하고 있었는지, 어디를 클릭했는지, 어디서 멈췄는지는 실제 세션을 봐야 안다. 유저가 버튼을 못 찾아서 화면을 마구 클릭하다 떠나는 장면, 폼을 입력하다가 에러가 나서 새로고침하는 장면. 이런 걸 보면 개선 방향이 바로 보인다.
PostHog는 이벤트 이탈 시점의 세션을 필터링해서 볼 수 있다. "결제 퍼널 3단계에서 이탈한 유저들의 세션만 보기" 같은 식으로.
개인정보 주의사항이 있다. 세션 리플레이에는 유저가 입력한 내용이 찍힐 수 있다. PostHog는 기본적으로 input, textarea 값을 마스킹하지만, 설정을 확인해야 한다.
posthog.init(POSTHOG_KEY, {
session_recording: {
maskAllInputs: true, // 모든 입력 마스킹
maskInputOptions: {
password: true, // 비밀번호는 반드시
email: true, // 이메일도 마스킹 권장
},
blockSelector: ".sensitive", // 특정 클래스 요소는 통째로 블록
},
});
무조건 다 찍는 게 능사가 아니다. 찍어야 할 것과 찍지 말아야 할 것을 구분하는 게 중요하다.
찍어야 할 것: 핵심 기능의 사용 여부, 퍼널 단계 완료/이탈, 오류 발생, 유료 전환 관련 행동.
찍지 말아야 할 것: 개인식별정보(이름, 이메일, 전화번호), 입력 내용 전체, 서비스와 무관한 브라우저 동작.
이벤트 이름 컨벤션 정리.
좋은 예:
project_created → 프로젝트 생성
report_exported → 리포트 내보내기
team_member_invited → 팀원 초대
subscription_upgraded → 구독 업그레이드
onboarding_step_completed → 온보딩 단계 완료
나쁜 예:
click → 뭘 클릭했는지 모름
button_clicked → 어떤 버튼인지 모름
user_action → 너무 광범위
pageView → 카멜케이스와 스네이크케이스 혼용
프로퍼티도 일관성 있게 유지하는 게 중요하다. report_id를 어떤 이벤트에선 reportId로, 다른 이벤트에선 report_id로 찍으면 나중에 쿼리할 때 지저분해진다.
데이터가 가득 찬 대시보드는 블랙박스가 달린 자동차와 같다. 사고가 나면 영상을 꺼내보면 된다. 뭘 실수했는지, 어디서 무언가 잘못됐는지. 단, 블랙박스를 달았다고 운전을 잘하게 되진 않는다. 데이터는 결정을 돕는 도구지, 결정을 대신해주지는 않는다.
허영 지표는 방향을 잡아주지 않는다. 가입자 수, 페이지뷰는 참고용이지 의사결정 근거가 아니다. 활성화율, 리텐션, 퍼널 이탈을 봐야 한다.
PostHog는 셀프호스팅이 가능한 올인원 프로덕트 애널리틱스 툴이다. 이벤트 트래킹, 퍼널, 세션 리플레이, 피처 플래그까지 하나로 된다. 초기 서비스에 비용 부담 없이 시작할 수 있다.
이벤트는 핵심 행동 위주로 설계한다. 모든 클릭을 찍으면 오히려 노이즈가 많아진다. 의사결정에 쓸 수 있는 이벤트만 정의하고, 이름 컨벤션을 통일한다.
유저 식별과 로그아웃 reset은 짝으로 다닌다. identify로 익명 세션과 유저를 연결하고, 로그아웃 시 reset으로 세션을 초기화한다.
세션 리플레이는 숫자가 설명 못 하는 맥락을 채워준다. 이탈률이 높은 단계에서 유저가 실제로 뭘 하는지 보면, 개선 방향이 명확해진다.
세 달 동안 아무도 안 쓰는 기능을 만들었던 건 데이터가 없어서가 아니라, 데이터를 볼 생각을 안 했기 때문이다. 지금은 기능을 만들기 전에 "이걸 어떤 이벤트로 측정할지"를 먼저 정한다. 측정할 수 없으면 만들지 않는다.