Supabase + Claude Code로 사용자 행동 분석 파이프라인 만들기
GA4로는 부족할 때. 제품 내부 이벤트를 Supabase에 쌓아 BI 없이 인사이트를 뽑는 법.
GA4·Amplitude 같은 도구는 좋지만 커스텀 이벤트·조인 분석에는 한계가 있다. Supabase의 Postgres를 이벤트 스토어로 쓰면 "내가 원하는 그대로의 분석"이 가능해진다.
왜 Supabase인가
- 데이터 소유: 이벤트 원본이 내 DB에 있다 (GA는 Google 서버)
- SQL 분석: BI 도구 없이 쿼리로 모든 분석 가능
- 비용: Free 티어로도 월 100만 이벤트 여유
- 연동 편함: 프론트·백엔드 이미 Supabase라면 별도 설정 거의 없음
이벤트 테이블 스키마
create table events (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users on delete set null,
session_id text not null,
event_name text not null,
properties jsonb default '{}'::jsonb,
created_at timestamptz default now()
);
create index idx_events_user_time on events(user_id, created_at desc);
create index idx_events_name_time on events(event_name, created_at desc);
create index idx_events_props on events using gin (properties);
핵심은 properties jsonb — 유연한 속성 저장.
프론트엔드 트래킹 헬퍼
// lib/track.ts
import { createClient } from "@/lib/supabase/client";
export async function track(name: string, props: Record<string, unknown> = {}) {
const supabase = createClient();
const { data: { session } } = await supabase.auth.getSession();
const sessionId = getSessionId(); // localStorage 기반
await supabase.from("events").insert({
user_id: session?.user.id ?? null,
session_id: sessionId,
event_name: name,
properties: props,
});
}
사용:
track("cta_click", { location: "home_hero", destination: "/contact" });
Claude Code로 분석 쿼리 뽑기
여기가 핵심이다. 쿼리를 Claude에게 위임한다.
프롬프트:
"events 테이블에서 지난 7일간
cta_click이벤트의 location 별 집계를 뽑아줘. 유니크 사용자 수와 클릭 수를 함께."
Claude 응답:
select
properties->>'location' as location,
count(*) as clicks,
count(distinct coalesce(user_id::text, session_id)) as unique_users,
round(count(*)::numeric / count(distinct coalesce(user_id::text, session_id)), 2) as clicks_per_user
from events
where event_name = 'cta_click'
and created_at >= now() - interval '7 days'
group by properties->>'location'
order by clicks desc;
퍼널 분석
프롬프트:
"홈 방문 → 서비스 페이지 → 문의 제출 퍼널의 각 단계 전환율을 뽑아줘"
Claude 응답 (CTE로):
with funnel as (
select
session_id,
max(case when event_name = 'page_view' and properties->>'path' = '/' then 1 else 0 end) as step1,
max(case when event_name = 'page_view' and properties->>'path' like '/services%' then 1 else 0 end) as step2,
max(case when event_name = 'contact_submit' then 1 else 0 end) as step3
from events
where created_at >= now() - interval '7 days'
group by session_id
)
select
sum(step1) as visited_home,
sum(step2) as visited_services,
sum(step3) as submitted_contact,
round(sum(step2)::numeric / nullif(sum(step1), 0) * 100, 1) as home_to_services_pct,
round(sum(step3)::numeric / nullif(sum(step2), 0) * 100, 1) as services_to_contact_pct
from funnel;
실시간 대시보드 구축
Supabase Realtime으로 라이브 모니터링:
const channel = supabase
.channel("events")
.on("postgres_changes",
{ event: "INSERT", schema: "public", table: "events" },
(payload) => updateDashboard(payload.new)
)
.subscribe();
어드민 페이지에서 방금 일어난 이벤트를 실시간으로 볼 수 있다.
GA4와의 역할 분담
| 도구 | 담당 |
|---|---|
| GA4 | 전체 트래픽·획득 채널·기본 전환 |
| Supabase events | 커스텀 플로우·유저별 journey·서버 이벤트 |
| Clarity | 세션 리플레이·히트맵 (UI 이슈 발견) |
세 개가 겹치지 않고 보완 관계. 각자 가장 잘하는 영역만 맡긴다.
주의할 점
- 개인정보 수집 금지 필드: 입력 값·내부 DB ID는 properties에 넣지 말 것
- 이벤트 수 폭증 방지: 스크롤·마우스 무브는 샘플링 또는 제외
- 보존 기간: 90일 이상 데이터는 월별 집계 테이블로 요약 후 삭제
결론: "GA로는 부족한데 BI는 과한" 중간 지점이 있다. Supabase + Claude Code가 정확히 그 자리를 채운다.
새 글을 이메일로 받아보세요
AI·데이터·서비스 기획 실전 글을 주 1-2회 발행합니다.
이런 글도 있어요
0→1 기획을 2주에 끝내는 법 — MVP 스코핑 체크리스트
아이디어에서 출시까지 2주. 개발에 들어가기 전 반드시 결정해야 하는 12개 질문과, 그 순서.
AI 시대 PM의 하루 — 4시간 업무를 2시간에 끝내는 Claude Code 워크플로우
회의 준비·리서치·스펙 문서·스크립트 자동화까지. PM의 반복 업무를 AI로 압축한 하루.
Claude Code로 MVP를 2주 안에 만드는 법
왜 하필 2주인가, 그리고 Week 1 / Week 2를 어떻게 쪼개는가. 예비창업자·1인사업가를 위한 실전 로드맵과 7가지 팁.