메이저맵
경력근거: 결제와 구독, 전환 퍼널 등을 실험 기반으로 유저가 가치를 느끼고 지갑을 여는 흐름
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고는 전환 퍼널을 실험 기반으로 설계하고 구현하는 일을 주요업무 첫 줄에 두고, 가설과 실험과 지표 읽기를 한 루프로 돌린다고 적으며, AI 서비스 자체를 만드는 자리가 아니라 기술을 고객 문제와 매출에 연결하는 자리라고 못박습니다. 이 포트폴리오는 그 기준을 그대로 씁니다. 모델을 쓰는 자리를 요약 문장 한 줄로 줄이고, 나머지는 이벤트 순서를 읽어 다음 실험을 정하는 규칙으로 채웁니다. 다만 이 포트폴리오는 이벤트 분석과 화면 구현을 보여줍니다. 진학 상담이나 교육 시장 가격 설계에 대한 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
온라인 비즈니스 모델을 제품으로 만들고 전환 퍼널을 실험 기반으로 설계·구현한다고 주요업무 첫 항목에 적습니다. 기능을 붙이는 일과 그 기능이 매출로 이어지는지 보는 일이 한 사람의 몫입니다.
가설을 세우고 실험을 설계하고 결과를 읽어 다음 결정을 내리는 루프를 함께 돌린다고 적혀 있습니다. 지표를 보는 사람과 만드는 사람이 나뉘지 않습니다.
AI 에이전트나 LLM 서비스 자체를 만드는 개발자를 찾는 것이 아니라, 새 기술을 빠르게 배워 고객 문제와 매출에 연결하는 사람을 찾는다고 공고가 직접 적습니다. 기술 선택의 기준이 처음부터 제품 성과입니다.
제품 담당자가 기간을 고르면, 진로 리포트 신청 흐름을 화면 단계로 나눠 각 단계에서 몇 명이 다음으로 넘어갔고 어디서 멈췄는지 보여줍니다. 멈춘 자리 판정은 세션별 이벤트 순서를 읽는 규칙이 정하고, 모델은 어느 단계에서 몇 명이 빠졌는지를 담당자가 읽을 한 줄로 요약합니다.
저는 진로 탐색 서비스의 제품을 맡고 있습니다. 유료 리포트를 붙였는데 신청이 기대만큼 안 옵니다. 전환율은 대시보드에 뜨지만 미리보기에서 나가는 건지 옵션 고르다 나가는 건지 알 수 없어, 다음에 무엇을 고칠지 회의에서 감으로 정합니다.
실험이 헛도는 이유는 데이터가 없어서가 아니라 전환율 한 숫자가 여러 화면의 문제를 덮기 때문입니다. 세션의 이벤트 순서를 화면 단계로 되돌려 멈춘 자리를 세어 주면, 다음 실험 대상이 목록에서 바로 나옵니다.
제품 담당자가 쓰는 화면 하나가 완결됩니다. 세션 이벤트를 화면 단계로 되돌려 단계별 진입과 이탈을 세고, 각 판정의 근거가 된 이벤트를 같이 보여줍니다. 실제 서비스 연동은 이벤트 파일 업로드로 대신하고 3주차로 미룹니다.
같은 기간의 두 실험군을 단계별로 비교해 어느 단계에서 차이가 벌어졌는지 냅니다. 세션 240건으로 집계가 두 번 세거나 빠뜨리지 않는지 확인합니다.
이벤트 수집 경로를 붙여 파일 업로드 없이 어제까지의 흐름이 화면에 뜹니다. 단계 정의를 화면에서 바꾸면 집계가 다시 돌아 새 정의로 비교됩니다.
제품 담당자가 기간을 고르면 단계별 진입과 이탈이 세어지고, 각 수치의 근거 이벤트가 같이 보입니다.
이벤트가 순서 없이 들어오거나 같은 세션이 두 번 올라오거나 정의에 없는 이벤트 이름이 섞인 입력 30종을 올려도 화면이 죽지 않고, 셀 수 없는 세션은 제외 사유와 함께 남습니다.
세션 240건으로 집계가 중복이나 누락 없이 서는지 확인합니다. 단, 정답과 데이터를 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
위 수치는 더미데이터 자가채점이며 실제 전환 개선 효과는 메이저맵의 실 이벤트와 실험 운영이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 이벤트 순서 뒤바뀜·세션 중복·정의에 없는 이벤트 이름을 더미에 일부러 섞어 쉬운 문제만 푸는 함정을 피합니다.
어느 단계에서 많이 빠졌다는 사실이 그 화면이 문제라는 뜻은 아닙니다. 유입 경로가 달라도 같은 자리에서 빠지므로 판정을 결론으로 읽지 않게 근거 이벤트를 항상 같이 보여줍니다.
실제 도입에서는 앱과 웹의 이벤트 이름이 갈리고 단계 정의가 자주 바뀝니다. 정의가 늘어나면 규칙을 코드에서 빼 설정으로 옮겨야 하며, 그것은 도입 단계 과제로 분리합니다.
결제 처리와 정산은 가맹점 계약이 있어야 다룰 수 있어 이 포트폴리오의 범위 밖입니다. 여기서는 신청 화면까지의 흐름을 세는 데까지만 다룹니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
리포트 신청 흐름에서 남은 세션별 이벤트입니다. 집계의 입력이며 순서가 뒤바뀐 이벤트와 세션 중복, 정의에 없는 이벤트 이름을 의도적으로 포함합니다.
| event_no | session_key | occurred_at | event_name_raw | seq | variant | device | referrer | note |
|---|---|---|---|---|---|---|---|---|
| EV-0001 | SES-7001 | 2026-08-12T10:02:11 | report_intro_view | 1 | A | web | search | — |
| EV-0002 | SES-7001 | 2026-08-12T10:03:40 | report_preview_open | 2 | A | web | — | — |
| EV-0003 | SES-7001 | 2026-08-12T10:05:02 | option_select | 3 | A | web | — | — |
| EV-0004 | SES-7001 | 2026-08-12T10:06:33 | apply_submit | 4 | A | web | — | 신청 완료 |
| EV-0005 | SES-7002 | 2026-08-12T11:20:05 | report_intro_view | 1 | B | app | push | — |
아래 JSON Schema 에 맞는 퍼널 이벤트 행을 더 만들어 주세요. {"type":"object","properties":{"event_no":{"type":"string"},"session_key":{"type":"string"},"occurred_at":{"type":"string"},"event_name_raw":{"type":"string"},"seq":{"type":["integer","null"]},"variant":{"type":"string","enum":["A","B","none"]},"device":{"type":"string","enum":["web","app"]},"referrer":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["event_no","session_key","occurred_at","event_name_raw","variant","device"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) seq 가 null 이라 순서를 시각으로만 잡아야 하는 행, (2) 시작 이벤트 없이 중간 단계부터 시작한 세션, (3) 같은 session_key 와 event_name_raw 가 두 번 적재된 중복 행, (4) 단계 정의에 없는 event_name_raw. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"event_no":{"type":"string"},"session_key":{"type":"string"},"occurred_at":{"type":"string"},"event_name_raw":{"type":"string"},"seq":{"type":["integer","null"]},"variant":{"type":"string","enum":["A","B","none"]},"device":{"type":"string","enum":["web","app"]},"referrer":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["event_no","session_key","occurred_at","event_name_raw","variant","device"]}| Next.js | 제품 담당자가 보는 화면입니다. 단계별 수치와 근거 이벤트를 한 화면에 둡니다. |
| Python | 이벤트 순서를 단계로 되돌리는 집계 러너의 언어입니다. |
| PostgreSQL | 세션과 이벤트를 담습니다. 세션 키와 이벤트 순번을 분리한 스키마가 집계의 정확도를 좌우합니다. |
| GCP | 3주차부터 이벤트 접수와 집계 실행을 맡습니다. |
| Flutter | 앱에서 들어온 이벤트도 같은 단계 정의로 세도록 이벤트 이름 규칙을 맞춥니다. |
| LLM | 단계별 집계가 끝난 뒤 가장 크게 빠진 단계와 그 인원을 한 줄로 요약합니다. 수치는 규칙이 냅니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 두 실험군을 단계별로 비교하는 단계로 넘어갑니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 이벤트를 직접 받아 단계 정의를 화면에서 바꾸는 단계로 확장합니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
stage_code사유 라벨 · session_key + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
applied신청 완료drop_at_preview미리보기 이탈drop_at_option옵션 이탈drop_at_intro소개 이탈event_unmapped이벤트 미매핑session_incomplete세션 불완전한 세션이 흐름의 어디까지 갔는지에 대한 판정입니다. 도달한 마지막 단계와 근거 이벤트를 함께 남깁니다.
{"type":"object","properties":{"result_id":{"type":"string"},"session_key":{"type":"string"},"variant":{"type":"string","enum":["A","B","none"]},"stage_code":{"type":"string","enum":["applied","drop_at_preview","drop_at_option","drop_at_intro","event_unmapped","session_incomplete"]},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{"type":["string","number","null"]}},"required":["field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["result_id","session_key","variant","stage_code","evidence","summary"]}[
{
"result_id": "sr_0001",
"session_key": "SES-7001",
"variant": "A",
"stage_code": "applied",
"evidence": [
{
"field": "event_name_raw",
"agg": null,
"value": "apply_submit"
},
{
"field": "event_no",
"agg": "count",
"value": 4
}
],
"summary": "네 단계를 모두 지나 신청까지 마쳤습니다.",
"hedge": null
},
{
"result_id": "sr_0002",
"session_key": "SES-7003",
"variant": "A",
"stage_code": "drop_at_option",
"evidence": [
{
"field": "event_name_raw",
"agg": null,
"value": "option_select"
},
{
"field": "seq",
"agg": null,
"value": null
}
],
"summary": "미리보기를 건너뛰고 옵션까지 갔다가 신청 없이 끝났습니다.",
"hedge": "합성 기준 판정이라 실제로는 미리보기 이벤트가 유실됐을 수 있습니다."
},
{
"result_id": "sr_0003",
"session_key": "SES-7002",
"variant": "B",
"stage_code": "drop_at_preview",
"evidence": [
{
"field": "event_name_raw",
"agg": null,
"value": "report_preview_open"
},
{
"field": "event_no",
"agg": "count",
"value": 2
}
],
"summary": "미리보기를 열고 다음 단계로 넘어가지 않았습니다. 같은 이벤트가 두 번 적재돼 한 번으로 셉니다.",
"hedge": null
},
{
"result_id": "sr_0004",
"session_key": "SES-7006",
"variant": "A",
"stage_code": "drop_at_option",
"evidence": [
{
"field": "event_name_raw",
"agg": null,
"value": "option_select"
},
{
"field": "occurred_at",
"agg": null,
"value": "2026-08-15T08:26:31"
}
],
"summary": "옵션 화면에서 8분을 머문 뒤 신청 없이 끝났습니다.",
"hedge": null
},
{
"result_id": "sr_0005",
"session_key": "SES-7004",
"variant": "B",
"stage_code": "event_unmapped",
"evidence": [
{
"field": "event_name_raw",
"agg": null,
"value": "리포트_미리보기"
},
{
"field": "note",
"agg": null,
"value": "정의에 없는 이벤트 이름"
}
],
"summary": "단계 정의에 없는 이벤트 이름이라 이 세션은 집계에서 뺍니다.",
"hedge": null
},
{
"result_id": "sr_0006",
"session_key": "SES-7005",
"variant": "none",
"stage_code": "session_incomplete",
"evidence": [
{
"field": "event_name_raw",
"agg": null,
"value": "option_select"
},
{
"field": "seq",
"agg": null,
"value": 2
}
],
"summary": "시작 이벤트 없이 중간부터 남아 있어 어디서 들어왔는지 정하지 못합니다.",
"hedge": null
}
][자동 검증 · 코드가 판정합니다] 1. stage_code 가 enum 여섯 값 중 하나인지 확인합니다. 2. result_id·session_key·variant·stage_code·evidence·summary 가 모두 채워졌는지 확인합니다. 3. seq 가 1 이상인지 확인합니다. 비어 있으면 시각 순서로 대신 잡습니다. 4. evidence 의 field 이름이 이벤트 데이터에 실재하는 컬럼인지 대조합니다. 5. agg 가 없는 evidence 의 value 가 그 session_key 행의 값과 같은지 확인합니다. 6. event_name_raw 가 단계 정의에 없으면 stage_code 는 event_unmapped 인지 확인합니다. 이벤트에는 정의에 있는 이름과 없는 이름이 모두 있습니다. 7. summary 가 존댓말로 끝나는지 확인합니다. [평가 러너 · 2주차] 세션 240건을 넣어 단계별 합계가 세션 수와 맞는지, 중복 적재된 세션이 두 번 세어지지 않는지 확인합니다. [수동 검증 · 사람이 봅니다] 1. 미리보기를 건너뛴 세션을 이탈로 볼지 정상 경로로 볼지 표본 20건을 눈으로 확인합니다. 2. 유입 경로별로 단계 정의를 달리 잡을지는 실험 설계를 따라 담당자가 정합니다.