“우리 서비스의 중요한 funnel 들이 무엇일까?” 라는 질문에 대해, 구성원 모두가 동일한 답을 할 수 있도록 만들고 싶습니다. product/business 를 개선 하기 위한 시도들 중 많은 것들을, funnel 에 대한 얘기로 표현 할 수 있기 때문 입니다.
이를 위해서, funnel 을 code 로 관리하는 모습을 그려보았습니다.
funnel 은 event 들로 구성 되고, 순서가 있습니다. 그러니 funnel 은 event 들의 ordered set 입니다. 그래서 이를 yaml 로 선언 할 수 있습니다.
name: onboardinghuman_readable_name: 온보딩steps:- first_open- sign_up- tutorial_completed
funnel 을 지칭 할 때 사용할 고유한 이름을 name 으로 적습니다. alphabet 소문자 + _ 의 조합 으로 적는 것이 좋겠습니다. funnel 을 구성 하는 event 들을 순서 대로 steps 에 적습니다.
이것만으로도,
“우리 서비스의 중요한 funnel 들이 무엇일까?” 라는 질문에 대해, 구성원 들이 모두 동일한 관점을 가지고 동일한 답을 할 수 있었으면 합니다.
라는 최초 목표는 달성 됩니다. funnel 들이 명확히 나열 되고, 각 funnel 의 steps 이 어떤 event 들로 구성되는 지도 명확 합니다.
조금 더 나아간다면, yaml file 을 읽는 것 보다 접근성이 좀 더 좋아지도록, 간단한 html 을 render 하고 web 에 hosting 할 수 있겠습니다.
여기서 더 나아갈 게 있을까요?
funnel 과 관련된 여러가지 값들을 확인할 수 있게 해주면 좋을 것 입니다.
funnel 을 구성하는 step 마다 값을 확인할 수 있고, 직전/최초 step 대비 proportion 을 보여주는, column chart 가 있으면 좋겠습니다. 아래는 amplitude 캡처 입니다.
를 지정 하면, sql 을 생성 하고, 실행 하여,
step | value | value_init | value_prev |
---|---|---|---|
first_open | 1,000 | 1,000 | |
sign_up | 750 | 1,000 | 1,000 |
tutorial_completed | 500 | 1,000 | 750 |
의 schema 의 result 를 return 하면 될 것 입니다.
몇 가지를 가정 합니다.
_date
column 이 존재 합니다.user_id
, session_id
column 이 존재 해야 합니다.이런 sql 을 생성 하면 됩니다.
withevent as (select user_id, 'first_open' as event_name, from event.first_open where _date between '2023-04-29' and '2023-04-29'union allselect user_id, 'sign_up' as event_name, from event.sign_up where _date between '2023-04-29' and '2023-04-29'union allselect user_id, 'tutorial_completed' as event_name, from event.tutorial_completed where _date between '2023-04-29' and '2023-04-29'), aggregated as (selectuser_id,[struct(1 as idx, 'first_open' as name, logical_or(event_name = 'first_open') as b),struct(2 as idx, 'sign_up' as name, logical_or(event_name = 'sign_up') as b),struct(3 as idx, 'tutorial_completed' as name, logical_or(event_name = 'tutorial_completed') as b)] as steps,fromeventgroup byuser_id)selectstep.name,countif(step.b) as value,lag(countif(step.b), 1) over (order by any_value(step.idx)) as value_prev,first_value(countif(step.b)) over (order by any_value(step.idx)) as value_init,any_value(step.idx) as idx,fromaggregated, unnest(steps) as stepgroup bynameorder byidx
를 입력 으로 받아서 sql 을 생성 할 수 있도록, sql 에 대한 jinja template 을 만듭니다.
withevent as ({% for step in steps %}select {{ entity }}, '{{ step.name }}' as event_name, from {{ step.get_table_id() }} where _date between '{{ date_s }}' and '{{ date_e }}'{% if not loop.last %}union all{% endif %}{%- endfor %}), aggregated as (select{{ entity }},[{%- for step in steps %}struct({{ loop.index }} as idx, '{{ step.name }}' as name, logical_or(event_name = '{{ step.name }}') as b){% if not loop.last %},{% endif %}{%- endfor %}] as steps,fromeventgroup by{{ entity }})selectstep.name,countif(step.b) as value,lag(countif(step.b), 1) over (order by any_value(step.idx)) as value_prev,first_value(countif(step.b)) over (order by any_value(step.idx)) as value_init,any_value(step.idx) as idx,fromaggregated, unnest(steps) as stepgroup bynameorder byidx
관련 code 는 github 에 있습니다.
추가로 구현 하고 보완 해야 할 것들이 있습니다.
현재 SQL 은 open funnel 로 집계 합니다. 그런데 이는 애매 할 수 있습니다. 예를 들어, 유저가 4/29 에 sign_up 했고 4/30 에 tutorial_completed 했을 때, 4/30 ~ 4/30 의 funnel 을 집계 하면, sign_up 을 셀 때는 포함 되지 못하고, tutorial_completed 는 +1 이 됩니다.
현재 구현한, x-axis 를 step 으로 하는 column chart 뿐 아니라, x-axis 를 시간 으로 하며 각 step-wise conversion rate 들을 series 로 그리는 line chart 도 조회 하고 싶을 것 입니다.
다음 글에서 다뤄 보도록 하겠습니다.