네이버웹툰
경력근거: 기존 데이터 플랫폼·파이프라인·분석 자산 위에서, 현업이 더 빠르고 정확하게 의사결정하도록 돕는 기능·자동화·도구 개발
네이버웹툰은 요일별 연재라는 약속 위에 서 있는 제품이고, 공고도 기존 데이터 플랫폼 위에서 현업이 더 빠르고 정확하게 판단하도록 돕는 도구를 만든다고 적었습니다. 회차가 약속한 요일에 안 열리는 손해는 요일 연재와 유료 미리보기를 함께 굴리는 플랫폼에서만 생기고, 편성표와 공개 로그가 따로 있는 회사라야 두 표를 견주는 판정이 성립합니다. 제가 여기서 보인 것은 편성 시각과 공개 로그를 분 단위로 맞추는 일, 그리고 그 결과를 아침에 읽는 화면입니다. 어떤 작품을 어느 요일에 붙일지, 미리보기 값을 얼마로 매길지는 편성과 사업의 판단이라 손대지 않았습니다. 요일 연재 바깥의 웹툰 이야기는 면접에서 물어보시면 아는 데까지만 답하겠습니다.
기존 데이터 플랫폼 위에서 현업이 더 빠르고 정확하게 판단하도록 돕는 기능과 도구를 만든다고 주요업무에 적혀 있습니다. 연재 담당자가 공개 시각을 눈으로 훑는 일을 화면 하나로 옮기는 것이 그 자리에 바로 놓입니다.
SQL 과 Python 실무 경험을 요건으로 두고 dbt·Airflow·Trino 같은 도구 경험을 우대로 적었습니다. 편성표와 공개 로그를 모델로 정리하고 공개 시각 직후에 판정을 돌리는 구조가 그 도구들 위에 그대로 올라갑니다.
AI Agent 와 LLM 도구로 데이터 지원 업무의 흐름을 다시 설계하고 반복 업무를 자동화한다고 적었습니다. 판정이 끝난 뒤 담당자가 읽을 한 문장을 모델이 쓰고 아침 리포트를 자동으로 내는 3주차가 그 방식에 붙습니다.
그 주 편성표와 회차 공개 로그, 작품 마스터를 받아 회차 하나하나에 다섯 판정 중 하나를 붙입니다. 로그는 UTC 로 찍히고 편성표는 담당자가 쓰는 KST 시각이라 아홉 시간을 맞춰 옮긴 뒤 분 단위로 견주고, 같은 회차 로그가 여러 줄이면 가장 이른 시각을 공개 시각으로 봅니다. 편성 비고에 「02:30 으로 미룸」처럼 담당자가 손으로 적어 둔 연기 표기가 있으면 그 시각을 읽어 기준 시각을 다시 잡고 견줍니다. 회차 번호가 편성표에 없거나 유료 설정이 어긋난 자리도 같은 화면에서 사유별로 나와, 담당자가 아침에 어디부터 손볼지 그 자리에서 정합니다.
연재 작품의 회차 공개를 챙기는 웹툰 운영 담당자
연재 작품이 수백 편이면 담당자가 월요일 0시 공개분을 앱에서 하나씩 열어 보는 방식으로는 몇 편밖에 못 봅니다. 편성표와 공개 로그를 시각 기준으로 맞춰 판정을 붙이면, 손볼 회차가 다섯 갈래로 나뉘어 독자가 빈 자리를 먼저 보는 일이 줄어듭니다.
편성표와 공개 로그, 작품 마스터를 올리면 회차마다 다섯 판정 중 하나가 붙고 사유별로 나뉘어 나오는 관리자 페이지입니다. 편성 비고에 적힌 연기 표기를 읽어 기준 시각을 다시 잡는 처리까지 이 주에 들어갑니다. 공개 로그는 사내 로그 적재 스키마를 가정한 파일로 받고, 앱 열람 기록이나 매출 집계는 다루지 않습니다.
회차 160건에 손으로 매긴 정답 판정을 붙여 자동 판정과 대조하고 사유별 일치율을 냅니다. 세 표를 dbt 모델로 옮겨 유일성과 결측에 테스트를 걸고 Trino 로 여러 저장소에 나뉜 공개 로그를 한 쿼리로 훑습니다.
Airflow 로 공개 시각 직후에 판정을 돌리고, 손볼 회차 목록과 사유별 건수를 담은 아침 리포트를 담당자에게 내는 단계입니다.
담당자가 그 주 편성표와 공개 로그를 올리면 손볼 회차가 사유별로 화면에 나오는 과정을 시연합니다.
회차 160건에 손으로 매긴 정답 판정을 붙여 대조하고 사유별 일치율을 냅니다. 편성표를 지은 것도 정답을 매긴 것도 저라서 이 일치율은 성능이 아니라, 더미 안에서 판정이 갈래별로 제대로 갈리는지를 보여 주는 값입니다.
판정을 붙이기 전에 일부러 망가뜨린 편성표와 로그 24가지를 먼저 통과시켜 봅니다. 로그 시각이 UTC 로만 적혀 있거나 유료 여부가 비어 있거나 같은 회차 로그가 중복으로 두 줄 들어오거나 편성 비고에서 옮길 시각을 못 읽어도 화면이 서고, 못 푼 값은 판정 자리 대신 hedge 로 내려갑니다.
예정 시각에서 정확히 60분 뒤에 열린 회차와 연재 첫 회차인 1화를 경계 값으로 넣어, 한 시간 기준이 어느 쪽으로 갈라지는지 화면에 세웁니다.
여기까지가 제가 실제로 재 본 것입니다. 일치율은 제 편성표에 제 정답을 붙여 나온 숫자이고, 독자가 그날 앱을 덜 닫았는지는 네이버웹툰의 공개 로그와 앱 열람 기록이 있어야 나옵니다. 실제 공개 로그가 들어오는 날 하루면 다시 재도록, 채점 러너와 정답 파일 모양을 지금 만들어 두었습니다.
사유별 일치율 뒤에는 실제 독자도 실제 공개 로그도 없습니다. 편성표를 짠 사람과 정답 판정을 매긴 사람이 둘 다 저라서 이 숫자는 성능이 아니라 이 화면이 제 더미 위에서 도는 범위를 가리킬 뿐이고, 네이버웹툰의 공개 로그가 들어오는 날 같은 러너를 그대로 돌려 다시 재겠습니다.
편성표를 짠 것도 정답 판정을 매긴 것도 저라서 잘 맞는 것은 자랑이 될 수 없습니다. 유료 여부가 빈 로그와 중복으로 찍힌 줄, 편성표에 없는 회차, 옮길 시각을 못 읽는 편성 비고를 먼저 섞어 놓고 화면을 붙였습니다.
이 포트폴리오는 시각 정렬과 판정 차례, 그 결과를 읽는 화면을 보여줍니다. 작품 추천이나 유료 미리보기 가격 정책의 깊이는 주장하지 않습니다. 판정은 분 단위 비교와 키 대조가 하고 모델은 담당자가 읽을 문장만 씁니다.
늦게 열린 것이 언제나 사고는 아닙니다. 작가 원고가 늦어 공지하고 미룬 회차도 같은 모양으로 걸리므로, 이 판정은 확정이 아니라 담당자가 열어 볼 후보로 냅니다.
실제 공개 시각은 서버가 회차를 연 시각과 독자 화면에 뜬 시각이 갈라집니다. 여기서는 로그에 찍힌 시각 하나만 보고, 노출 시각까지 견주는 일은 도입 단계 과제로 둡니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
담당자가 미리 짜 둔 그 주 회차 편성표입니다. 판정의 기준 시각이 여기서 나오고, 편성 담당자 칸이 비어 있는 자동 편성 줄을 담아 사람 손이 안 닿은 회차도 함께 판정되는 것을 보입니다. 편성 비고는 담당자가 자유롭게 적는 칸이라 「02:30 으로 미룸」처럼 시각이 드러난 줄과 시각을 못 읽는 줄을 함께 담았습니다.
| title_id | episode_no | planned_open_at | is_paid_planned | planned_by | plan_note |
|---|---|---|---|---|---|
| W-2041 | 137 | 2026-09-21 00:00 | true | 편성A | — |
| W-1187 | 204 | 2026-09-21 00:00 | true | 편성A | 작가 연락 대기 |
| W-3320 | 58 | 2026-09-21 00:00 | true | 편성B | — |
| W-2765 | 91 | 2026-09-21 00:00 | false | 편성B | 작가 사정으로 이번 주만 02:30 으로 미룸(공지 완료) |
| W-4408 | 12 | 2026-09-21 00:00 | true | — | — |
아래 JSON Schema 에 맞는 편성표 행을 더 만들어 주세요. {"type":"object","properties":{"title_id":{"type":"string"},"episode_no":{"type":"integer","minimum":1},"planned_open_at":{"type":"string"},"is_paid_planned":{"type":"boolean"},"planned_by":{"type":["string","null"]},"plan_note":{"type":["string","null"]}},"required":["title_id","episode_no","planned_open_at","is_paid_planned","planned_by","plan_note"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) planned_by 가 null 인 자동 편성 줄, (2) 0시가 아니라 21시처럼 다른 시각에 공개하는 작품, (3) 연재 첫 회차인 episode_no 1, (4) 같은 작품의 지난 주차 회차, (5) plan_note 에 「02:30 으로 미룸」처럼 옮길 시각이 드러난 줄, (6) plan_note 가 적혀 있지만 옮길 시각을 읽을 수 없는 줄. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"title_id":{"type":"string"},"episode_no":{"type":"integer","minimum":1},"planned_open_at":{"type":"string"},"is_paid_planned":{"type":"boolean"},"planned_by":{"type":["string","null"]},"plan_note":{"type":["string","null"]}},"required":["title_id","episode_no","planned_open_at","is_paid_planned","planned_by","plan_note"]}회차가 실제로 열린 시각이 찍힌 서비스 로그입니다. 시각은 UTC 로 적히고, 유료 여부가 비어 있는 줄과 같은 회차가 중복으로 두 번 찍힌 줄, 편성표에 없는 회차 줄을 일부러 담았습니다.
| title_id | episode_no | opened_at | is_paid_actual | open_channel |
|---|---|---|---|---|
| W-2041 | 137 | 2026-09-20T17:47:12Z | true | app |
| W-2765 | 91 | 2026-09-20T17:35:00Z | false | app |
| W-3320 | 58 | 2026-09-20T15:00:08Z | false | app |
| W-0912 | 137 | 2026-09-20T15:02:41Z | true | app |
| W-4408 | 12 | 2026-09-20T15:00:11Z | true | app |
아래 JSON Schema 에 맞는 공개 로그 행을 더 만들어 주세요. {"type":"object","properties":{"title_id":{"type":"string"},"episode_no":{"type":"integer","minimum":1},"opened_at":{"type":"string"},"is_paid_actual":{"type":["boolean","null"]},"open_channel":{"type":"string","enum":["app","web"]}},"required":["title_id","episode_no","opened_at","is_paid_actual","open_channel"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) is_paid_actual 이 null 인 줄, (2) 같은 title_id·episode_no 가 두 번 찍힌 줄, (3) episode_schedule 에 없는 title_id·episode_no 줄, (4) 예정 시각과 정확히 60분 차이가 나는 줄. opened_at 은 언제나 UTC 표기로 쓰고, 생성한 행은 반드시 위 스키마를 통과해야 합니다.{"type":"object","properties":{"title_id":{"type":"string"},"episode_no":{"type":"integer","minimum":1},"opened_at":{"type":"string"},"is_paid_actual":{"type":["boolean","null"]},"open_channel":{"type":"string","enum":["app","web"]}},"required":["title_id","episode_no","opened_at","is_paid_actual","open_channel"]}작품마다 이름과 연재 요일, 연재 시각을 적은 기준 표입니다. 화면에 뜨는 작품 이름이 여기서 오고, 편성표의 예정일 요일이 이 표의 연재 요일과 다르면 weekday_match 를 거짓으로 두고 그 사실을 hedge 로 남깁니다.
| title_id | title_name | publish_weekday | publish_time_kst | genre |
|---|---|---|---|---|
| W-2041 | 붉은 등대의 주인 | 월 | 00:00 | 미스터리 |
| W-1187 | 하숙집 고양이 | 월 | 00:00 | 일상 |
| W-3320 | 검을 삼킨 아이 | 월 | 00:00 | 판타지 |
| W-0912 | 야근하는 성좌 | 월 | 00:00 | 로맨스 |
| W-2765 | 바다 건너 편지 | 월 | 00:00 | 드라마 |
아래 JSON Schema 에 맞는 작품 마스터 행을 더 만들어 주세요. {"type":"object","properties":{"title_id":{"type":"string"},"title_name":{"type":"string"},"publish_weekday":{"type":"string","enum":["월","화","수","목","금","토","일"]},"publish_time_kst":{"type":"string"},"genre":{"type":["string","null"]}},"required":["title_id","title_name","publish_weekday","publish_time_kst","genre"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) genre 가 null 인 줄, (2) 0시가 아닌 시각에 연재하는 작품, (3) 편성표의 예정 요일과 다른 연재 요일을 가진 작품, (4) 이름이 서로 비슷해 사람이 헷갈리는 작품 쌍. 한 title_id 는 한 줄만 두고, 생성한 행은 반드시 위 스키마를 통과해야 합니다.{"type":"object","properties":{"title_id":{"type":"string"},"title_name":{"type":"string"},"publish_weekday":{"type":"string","enum":["월","화","수","목","금","토","일"]},"publish_time_kst":{"type":"string"},"genre":{"type":["string","null"]}},"required":["title_id","title_name","publish_weekday","publish_time_kst","genre"]}| Python | 편성표와 공개 로그를 읽어 시각을 KST 로 맞추고, 편성 비고에 적힌 연기 표기를 읽어 기준 시각을 다시 잡은 뒤 판정 차례를 따라 회차마다 하나만 남기는 규칙 엔진의 언어입니다. |
| SQL | 작품·회차 단위 집계와 사유별 건수를 내는 질의를 씁니다. 같은 회차 로그 가운데 가장 이른 시각을 고르는 것도 여기서 합니다. |
| dbt | 편성표·공개 로그·작품 마스터를 판정이 바로 읽는 모델로 정리하고, title_id 와 episode_no 짝의 유일성과 결측에 테스트를 겁니다. |
| Airflow | 공개 시각 직후에 판정을 돌리고 아침 리포트를 내는 일정을 맡습니다. 실패한 날은 다시 돌려 그날 판정을 채웁니다. |
| Trino | 여러 저장소에 나뉜 공개 로그를 한 쿼리로 훑어 지난 주차까지 거슬러 판정을 다시 매길 때 씁니다. |
| LLM | 판정이 정해진 뒤 늦어진 회차를 작품 이름과 시각으로 되짚는 한 문장을 씁니다. |
| AI Agent | 3주차에 판정 결과를 읽고 담당자에게 보낼 아침 리포트 초안과 확인할 회차 목록을 만듭니다. |
✗ fail → 판정이 안 붙은 회차가 1할을 넘으면 읽는 시각 표기를 먼저 좁히고 그때까지는 그 회차를 목록에 따로 세웁니다.
✓ pass → 2주차 정답 대조와 dbt 모델 이전으로 넘어갑니다.
✗ fail → 갈린 회차만 모아 한 시간 문턱과 비고를 읽는 범위를 다시 잡습니다.
✓ pass → 3주차 Airflow 자동 실행과 아침 리포트로 넘어갑니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · check_no + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
제때 열림제때 열림늦게 열림늦게 열림아직 안 열림안 열림일정에 없던 회차일정 밖 회차유료 설정이 다름유료 설정 차이회차 하나가 편성표의 예정 시각과 실제 공개 로그를 거쳐 어떤 판정을 받는지 남기는 형식입니다. 판정은 규칙이 정하고 요약 문장만 모델이 씁니다.
{"type":"object","properties":{"check_no":{"type":"string"},"title_id":{"type":"string"},"title_name":{"type":["string","null"]},"episode_no":{"type":"integer","minimum":1},"verdict":{"type":"string","enum":["제때 열림","늦게 열림","아직 안 열림","일정에 없던 회차","유료 설정이 다름"]},"planned_open_at":{"type":["string","null"]},"effective_planned_at":{"type":["string","null"],"description":"planned_open_at 에 편성 비고(plan_note)의 연기 표기를 얹어 다시 잡은 기준 시각입니다. 비고가 없거나 옮길 시각을 못 읽으면 planned_open_at 을 그대로 씁니다."},"first_opened_at_kst":{"type":["string","null"],"description":"같은 title_id·episode_no 로그 가운데 가장 이른 opened_at 을 KST(+09:00)로 옮겨 적은 시각입니다."},"delay_minutes":{"type":["integer","null"],"description":"first_opened_at_kst 에서 effective_planned_at 을 뺀 분입니다. 편성표나 로그 한쪽이 없으면 비웁니다."},"weekday_match":{"type":["boolean","null"],"description":"planned_open_at 의 요일이 title_master.publish_weekday 와 같으면 참입니다. 편성표나 작품 마스터 한쪽이 없으면 비웁니다."},"paid_match":{"type":["boolean","null"],"description":"is_paid_planned 와 is_paid_actual 이 같으면 참입니다. is_paid_actual 이 비어 있으면 비웁니다."},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{}},"required":["field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["check_no","title_id","episode_no","verdict","evidence","summary"]}[
{
"check_no": "W-2041-137",
"title_id": "W-2041",
"title_name": "붉은 등대의 주인",
"episode_no": 137,
"verdict": "늦게 열림",
"planned_open_at": "2026-09-21 00:00",
"effective_planned_at": "2026-09-21 00:00",
"first_opened_at_kst": "2026-09-21 02:47",
"delay_minutes": 167,
"weekday_match": true,
"paid_match": true,
"evidence": [
{
"field": "planned_open_at",
"value": "2026-09-21 00:00"
},
{
"field": "opened_at",
"value": "2026-09-20T17:47:12Z"
},
{
"field": "title_name",
"value": "붉은 등대의 주인"
},
{
"field": "opened_at",
"agg": "count",
"value": 2
}
],
"summary": "붉은 등대의 주인 137화가 월요일 0시 예정인데 2시 47분에 열려 새벽에 앱을 연 독자는 빈 자리를 봤습니다.",
"hedge": "같은 회차 로그가 중복으로 두 번 남아 있어 먼저 찍힌 02:47 을 공개 시각으로 봤습니다."
},
{
"check_no": "W-1187-204",
"title_id": "W-1187",
"title_name": "하숙집 고양이",
"episode_no": 204,
"verdict": "아직 안 열림",
"planned_open_at": "2026-09-21 00:00",
"effective_planned_at": "2026-09-21 00:00",
"first_opened_at_kst": null,
"delay_minutes": null,
"weekday_match": true,
"paid_match": null,
"evidence": [
{
"field": "planned_open_at",
"value": "2026-09-21 00:00"
},
{
"field": "title_name",
"value": "하숙집 고양이"
},
{
"field": "plan_note",
"value": "작가 연락 대기"
},
{
"field": "opened_at",
"agg": "count",
"value": 0
}
],
"summary": "하숙집 고양이 204화가 월요일 0시 예정인데 점검 시각 3시 10분까지 공개 로그가 한 건도 없습니다.",
"hedge": "편성 비고가 「작가 연락 대기」뿐이라 옮길 시각을 못 읽었고, 예정 시각 0시를 그대로 기준으로 썼습니다."
},
{
"check_no": "W-2765-91",
"title_id": "W-2765",
"title_name": "바다 건너 편지",
"episode_no": 91,
"verdict": "제때 열림",
"planned_open_at": "2026-09-21 00:00",
"effective_planned_at": "2026-09-21 02:30",
"first_opened_at_kst": "2026-09-21 02:35",
"delay_minutes": 5,
"weekday_match": true,
"paid_match": true,
"evidence": [
{
"field": "plan_note",
"value": "작가 사정으로 이번 주만 02:30 으로 미룸(공지 완료)"
},
{
"field": "planned_open_at",
"value": "2026-09-21 00:00"
},
{
"field": "opened_at",
"value": "2026-09-20T17:35:00Z"
},
{
"field": "title_name",
"value": "바다 건너 편지"
}
],
"summary": "바다 건너 편지 91화가 공지한 대로 2시 30분으로 미뤄졌고 2시 35분에 열려 5분 차이로 들어왔습니다.",
"hedge": "편성 비고의 「02:30 으로 미룸」을 읽어 기준 시각을 옮겼습니다. 비고를 안 읽었다면 155분 늦음으로 걸렸을 회차입니다."
},
{
"check_no": "W-1530-77",
"title_id": "W-1530",
"title_name": "옥탑방 연금술사",
"episode_no": 77,
"verdict": "제때 열림",
"planned_open_at": "2026-09-21 00:00",
"effective_planned_at": "2026-09-21 00:00",
"first_opened_at_kst": "2026-09-21 00:00",
"delay_minutes": 0,
"weekday_match": false,
"paid_match": true,
"evidence": [
{
"field": "planned_open_at",
"value": "2026-09-21 00:00"
},
{
"field": "opened_at",
"value": "2026-09-20T15:00:19Z"
},
{
"field": "publish_weekday",
"value": "토"
},
{
"field": "title_name",
"value": "옥탑방 연금술사"
}
],
"summary": "옥탑방 연금술사 77화가 예정 시각 0시에 맞춰 열렸습니다.",
"hedge": "작품 마스터의 연재 요일은 토요일인데 이번 편성은 월요일에 잡혔습니다. 임시 편성인지 마스터가 낡은 것인지는 담당자가 정하라고 판정은 그대로 두고 이 줄만 남깁니다."
},
{
"check_no": "W-0912-137",
"title_id": "W-0912",
"title_name": "야근하는 성좌",
"episode_no": 137,
"verdict": "일정에 없던 회차",
"planned_open_at": null,
"effective_planned_at": null,
"first_opened_at_kst": "2026-09-21 00:02",
"delay_minutes": null,
"weekday_match": null,
"paid_match": null,
"evidence": [
{
"field": "title_id",
"value": "W-0912"
},
{
"field": "episode_no",
"value": 137
},
{
"field": "opened_at",
"value": "2026-09-20T15:02:41Z"
},
{
"field": "episode_no",
"agg": "count",
"value": 0
}
],
"summary": "야근하는 성좌 137화가 0시 2분에 열렸는데 이번 주 편성표에는 그 회차 줄이 없습니다.",
"hedge": "편성 입력이 늦은 것인지 회차 번호가 잘못 찍힌 것인지는 담당자가 정합니다."
},
{
"check_no": "W-3320-58",
"title_id": "W-3320",
"title_name": "검을 삼킨 아이",
"episode_no": 58,
"verdict": "유료 설정이 다름",
"planned_open_at": "2026-09-21 00:00",
"effective_planned_at": "2026-09-21 00:00",
"first_opened_at_kst": "2026-09-21 00:00",
"delay_minutes": 0,
"weekday_match": true,
"paid_match": false,
"evidence": [
{
"field": "is_paid_planned",
"value": true
},
{
"field": "is_paid_actual",
"value": false
},
{
"field": "title_name",
"value": "검을 삼킨 아이"
},
{
"field": "opened_at",
"value": "2026-09-20T15:00:08Z"
}
],
"summary": "검을 삼킨 아이 58화가 예정대로 0시에 열렸지만 유료 미리보기로 걸려야 할 회차가 무료로 공개됐습니다.",
"hedge": null
},
{
"check_no": "W-6620-1",
"title_id": "W-6620",
"title_name": "늦여름 소나기",
"episode_no": 1,
"verdict": "제때 열림",
"planned_open_at": "2026-09-21 00:00",
"effective_planned_at": "2026-09-21 00:00",
"first_opened_at_kst": "2026-09-21 01:00",
"delay_minutes": 60,
"weekday_match": true,
"paid_match": true,
"evidence": [
{
"field": "planned_open_at",
"value": "2026-09-21 00:00"
},
{
"field": "opened_at",
"value": "2026-09-20T16:00:00Z"
},
{
"field": "episode_no",
"value": 1
},
{
"field": "title_name",
"value": "늦여름 소나기"
}
],
"summary": "늦여름 소나기 1화가 예정보다 60분 뒤에 열렸고 한 시간을 넘지 않아 제때 열린 쪽으로 갈라집니다.",
"hedge": "60분은 늦음 기준 바로 아래 경계 값이라 이 회차는 손볼 곳 목록에 올라오지 않습니다."
},
{
"check_no": "W-3871-149",
"title_id": "W-3871",
"title_name": "지하철 9번 출구",
"episode_no": 149,
"verdict": "제때 열림",
"planned_open_at": "2026-09-21 00:00",
"effective_planned_at": "2026-09-21 00:00",
"first_opened_at_kst": "2026-09-21 00:00",
"delay_minutes": 0,
"weekday_match": true,
"paid_match": null,
"evidence": [
{
"field": "planned_open_at",
"value": "2026-09-21 00:00"
},
{
"field": "opened_at",
"value": "2026-09-20T15:00:05Z"
},
{
"field": "title_name",
"value": "지하철 9번 출구"
},
{
"field": "is_paid_actual",
"value": null
}
],
"summary": "지하철 9번 출구 149화가 0시에 열려 편성표에 적힌 시각과 같습니다.",
"hedge": "공개 로그에 유료 여부가 비어 있어 유료 설정 비교는 건너뛰었습니다."
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 다섯 값 중 하나인지 확인합니다. 규칙이 정한 값이라 모델이 새 값을 지어내면 그 응답을 버립니다. 2. check_no·title_id·episode_no·verdict·evidence·summary 필드가 빠짐없이 존재하는지 확인합니다. 3. episode_no 가 1 이상인지 확인합니다. 4. effective_planned_at 을 편성 비고에서 다시 읽습니다. plan_note 에 「02:30 으로 미룸」·「2시 30분으로 미룸」처럼 옮길 시각이 드러난 표기가 있으면 편성일의 그 시각을 기준 시각으로 쓰고, 시각을 못 읽은 비고는 planned_open_at 을 그대로 두고 원문을 hedge 에 남깁니다. plan_note 가 비어 있으면 planned_open_at 이 곧 기준 시각입니다. 5. delay_minutes 가 first_opened_at_kst 에서 effective_planned_at 을 뺀 분과 같은지 다시 계산합니다. 로그의 opened_at 은 UTC 로 적히므로 아홉 시간을 더해 KST 로 옮긴 뒤 뺍니다. 6. 늦게 열림은 delay_minutes 가 60 을 넘는지 다시 계산합니다. 다만 delay_minutes 가 60 과 같으면 제때 열림으로 둡니다. 7. 아직 안 열림은 effective_planned_at 이 점검 시각보다 앞서고 그 title_id·episode_no 의 opened_at 이 한 건도 없는지 다시 계산합니다. 점검 시각은 실행할 때 주어지고 더미 시연에서는 2026-09-21 03:10(KST)을 씁니다. 기준 시각이 점검 시각보다 뒤인 회차는 아직 판정할 때가 아니므로 그 주 목록에서 빼 두고, 더미도 그 시각 이전 편성만 담습니다. 8. 일정에 없던 회차는 그 title_id·episode_no 짝이 episode_schedule 에 한 줄도 없는지 다시 계산합니다. 9. 유료 설정이 다름은 is_paid_planned 와 is_paid_actual 이 다른지 다시 계산합니다. 다만 is_paid_actual 이 비어 있으면 판정하지 않고 그 사실을 hedge 에 남깁니다. 10. 같은 title_id·episode_no 로그가 중복으로 두 줄 이상이면 가장 이른 opened_at 을 공개 시각으로 씁니다. 11. weekday_match 는 planned_open_at 의 요일을 title_master.publish_weekday 와 견줘 채웁니다. 다르면 거짓을 두고 그 사실을 hedge 에 적되 판정은 바꾸지 않습니다 — 임시 편성일 수도 있어 담당자가 읽을 단서로만 냅니다. 편성표나 작품 마스터 한쪽이 없으면 비웁니다. 12. evidence 의 field 이름이 입력에 있는 컬럼인지 대조합니다. 없는 이름을 인용하면 요약이 지어낸 문장이라는 뜻이라 규칙이 만든 기본 문장으로 되돌립니다. 13. evidence 의 value 가 수록된 행의 원문 값과 같은지 확인합니다. 여러 행을 세어 얻은 수치는 agg 를 함께 적습니다. 14. summary 가 존댓말 한 문장으로 끝나는지 확인합니다. [판정 차례 · 위 규칙이 둘 이상 동시에 걸릴 때] 한 회차에는 판정을 하나만 남깁니다. ① 일정에 없던 회차 → ② 아직 안 열림 → ③ 유료 설정이 다름 → ④ 늦게 열림 → ⑤ 나머지는 제때 열림. 위 6~9번은 「~면 그 판정」이 아니라 「~면 그 후보」로 읽고, 실제 값은 이 차례에서 가장 먼저 걸린 것으로 정합니다. weekday_match 는 이 차례에 끼지 않고 hedge 로만 남습니다. [평가 러너 · 2주차] 손으로 매긴 정답 판정 160건과 자동 판정을 대조해 사유별 일치율을 내고, 어긋난 자리는 따로 모아 한 시간 기준과 판정 차례를 다시 봅니다. 편성표에 없는 회차를 일부러 심어 두고 심은 개수와 잡은 개수를 견주는 일, 그리고 비고에서 옮길 시각을 몇 건이나 읽어 냈는지 세는 일도 이 러너에서 합니다. [수동 검증 · 사람이 봅니다] 1. 늦게 열림으로 걸린 회차 20건을 열어 작가 원고 지연이나 공지된 휴재였는지 담당자가 판단합니다. 2. 유료 설정이 다름으로 걸린 회차가 프로모션 무료 공개였는지 확인하고 판정 차례를 다시 봅니다. 3. weekday_match 가 거짓으로 남은 회차를 모아 임시 편성이었는지 작품 마스터가 낡은 것인지 담당자가 정합니다.