Supabase + Claude Code로 사용자 행동 분석 파이프라인 만들기
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회 발행합니다.
이런 글도 있어요
기업 AI 교육, 어떻게 도입해야 실패하지 않나요? 담당자를 위한 결정 가이드
교육 벤더가 아니라 도입 담당자 관점의 기준: 실패 패턴 3가지, 도입하면 안 되는 조직 조건, 사내망 점검 5항목, 파일럿 성공·실패 기준, 벤더에게 물어볼 질문 7가지.
SaaS UX 감사, 무엇을 점검해야 하나요? 실무에서 쓰는 12차원 체크리스트
결함 리스트가 아니라 12차원 × 여정 매트릭스로 감사하는 법: 차원별 점검 질문과 나쁜 신호, 페르소나 3종 실측 절차, 흔한 함정 4가지.
화면정의서, AI로 자동 생성할 수 있나요? 실무 파이프라인과 품질 기준
문서 생성형·코드 스캔형·오버레이형 비교, 50개 이상 라우트 실제 운영 파이프라인 4단계, AI가 자주 틀리는 검수 포인트, 품질 체크리스트.