用代码固定定义并重新计算指标
한국어 원문으로 표시합니다.
목표
'모아노트' 16주치 제품 기록에서 DAU·WAU·MAU·스티키니스·주간 공유 사용자·7일 활성화율을 정의대로 계산하는 함수를 만들고, 정의가 바뀔 때 숫자가 얼마나 흔들리는지 잰다.
왜 중요한가
지표는 정의가 적혀 있을 때만 숫자다. 내부 계정·시간대·중복·활성의 기준 가운데 하나만 달라져도 같은 기록에서 전혀 다른 추세가 나온다. 창업 초기에는 이 숫자로 투자와 채용을 결정하므로, 정의를 코드로 못박고 다시 계산해 같은 값이 나오게 하는 습관이 제품 판단의 바닥이다.
재료와 정의
/opt/fixtures/founder/product/users.csv—user_id,signup_at,is_internal(시각은 UTC, 끝에 Z)/opt/fixtures/founder/product/events.csv—user_id,ts,event(event: session_start · create_doc · share_doc · invite_sent · upgrade)- 사업 시간대는 Asia/Seoul(UTC+9). 날짜는 서울 날짜, 주는 서울 시각 월요일 00:00 시작이고 0주차는 2026-03-02 다(0~15주차).
- 내부 계정(is_internal=1)은 모든 지표에서 뺀다. 활성 = 그 기간에
session_start가 1번 이상. 사람 수로 센다. - 모든 함수는 첫 인자로 재료 디렉터리 경로를 받는다(채점기가 다른 재료로도 부른다). 날짜 인자는
"2026-06-14"같은 문자열이다.
단계
/root/founder/metrics/summary.json에 재료 개요를 쓴다:users(전체 사용자 수),internal_users,events(전체 사건 줄 수),event_counts(사건 종류별 줄 수 객체)./root/founder/metrics/metrics.py에dau(fix, day)를 만든다 — 서울 날짜day에 session_start 가 있는 외부 사용자 수(정수)./root/founder/metrics/internal.json에 2026-06-14 의dau_all(내부 계정 포함),dau_external(제외),inflation_pct(= (dau_all − dau_external) / dau_external × 100, 소수 둘째 자리 반올림)을 쓴다.metrics.py에wau(fix, day)·mau(fix, day)를 더한다 —day를 포함해 거슬러 7일·28일 안의 활성 외부 사용자 수.metrics.py에stickiness(fix, day)를 더한다 —day까지 28일 동안의 DAU 평균 ÷mau(fix, day), 소수 넷째 자리 반올림./root/founder/metrics/definitions.json에 2026-06-14 기준 WAU 를 세 정의로 쓴다:session(session_start),any_event(사건 종류 무관),core(share_doc). 모두 외부 사용자, 사람 수.metrics.py에weekly_sharers(fix)를 더한다 — 0~15주차 각각의 '그 주에 share_doc 을 한 외부 사용자 수' 를{"0": n, "1": n, ...}처럼 문자열 주차 키의 딕셔너리로 돌려준다.metrics.py에activation(fix)를 더하고(가입 주 0~11 의 외부 사용자 중 가입 시각부터 7×24시간 안에 create_doc 을 한 비율 →{"signups": n, "activated": m, "rate": 0.0000}),/root/founder/metrics/report.json에 2026-06-14 의dau·wau·mau·stickiness,activation_rate,north_star_peak_week(주간 공유 사용자가 가장 많은 주차, 정수),north_star_week15(15주차 값)를 모아 쓴다.
참고
- 서울 날짜:
datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc).astimezone(ZoneInfo("Asia/Seoul")).date() - 주차:
(서울시각 - datetime(2026, 3, 2, tzinfo=서울)).days // 7 - 흔한 실수: UTC 날짜로 묶기, 사건 수를 사람 수로 적기, 내부 계정을 빼지 않기, 30일이나 달력의 달로 MAU 를 세기, 스티키니스를 하루치 DAU 로 나누기.
- 표준 라이브러리(csv·datetime·zoneinfo·collections)만으로 충분하다. sqlite3 CLI 로 확인해 봐도 좋다.
재료를 먼저 센다
/root/founder/metrics/summary.json 에 users·internal_users·events·event_counts 를 쓴다.
csv.DictReader 로 두 파일을 읽고 collections.Counter 로 사건 종류를 세면 됩니다. is_internal 은 문자열 "1" 입니다.
DAU — 서울 날짜, 외부 사용자, 사람 수
/root/founder/metrics/metrics.py 에 dau(fix, day) 를 만든다. 서울 날짜 day 에 session_start 가 있는 외부 사용자 수.
UTC 시각을 Asia/Seoul 로 바꾼 뒤 .date() 로 묶고, 사용자 id 를 set 에 모아 크기를 셉니다. 내부 계정은 users.csv 의 is_internal 로 거릅니다.
내부 계정이 지표를 얼마나 부풀리나
/root/founder/metrics/internal.json 에 2026-06-14 의 dau_all·dau_external·inflation_pct 를 쓴다.
같은 날·같은 정의(session_start)에서 내부 계정 포함 여부만 바꿔 두 번 셉니다. 비율의 분모는 외부 사용자 DAU 입니다.
WAU·MAU — 기준일을 포함한 7일·28일
metrics.py 에 wau(fix, day)·mau(fix, day) 를 더한다. day 를 포함해 거슬러 7일·28일 안의 활성 외부 사용자 수.
날짜별 활성 집합을 만들어 두고 day, day-1, …, day-6(또는 -27)의 합집합 크기를 셉니다. 달력의 달이나 30일이 아닙니다.
스티키니스 — 28일 평균 DAU ÷ MAU
metrics.py 에 stickiness(fix, day) 를 더한다. day 까지 28일 동안의 DAU 평균을 mau(fix, day) 로 나누고 소수 넷째 자리로 반올림.
그날 하루의 DAU 로 나누면 요일에 따라 출렁입니다. 28일 각각의 DAU 를 더해 28 로 나눈 평균을 씁니다.
활성의 정의만 바꿔 같은 창을 센다
/root/founder/metrics/definitions.json 에 2026-06-14 기준 7일 창의 외부 사용자 수를 session·any_event·core(share_doc) 세 정의로 쓴다.
창과 사용자 필터는 그대로 두고 세는 사건 종류만 바꿉니다. any_event 는 사건 종류를 가리지 않습니다.
North Star 후보 — 주간 공유 사용자
metrics.py 에 weekly_sharers(fix) 를 더한다. 0~15주차 각각의 share_doc 외부 사용자 수를 문자열 주차 키 딕셔너리로 돌려준다.
주차는 (서울 시각 − 2026-03-02 00:00 서울).days // 7 입니다. 사건이 없는 주도 0 으로 16개 키를 모두 채웁니다.
활성화율과 한 장 보고서
metrics.py 에 activation(fix) 를 더하고 /root/founder/metrics/report.json 에 dau·wau·mau·stickiness·activation_rate·north_star_peak_week·north_star_week15 를 모은다.
활성화는 달력 날짜가 아니라 가입 시각부터 7×24시간(timedelta(days=7)) 안의 첫 create_doc 입니다. 대상은 가입 주 0~11 의 외부 사용자입니다. 가장 많은 주가 여럿이면 앞 주차를 고릅니다.