에이치티비욘드
경력근거: 광고 및 공지 송출을 위한 CMS 설계 및 개발
※ 실제 산출물이 아닌 AI 예측 시안입니다.
에이치티비욘드의 IoT팀은 아파트 단지 안의 키오스크·POS·DID·사이니지를 만들면서 그 위에 광고와 공지를 내보내는 CMS 까지 함께 맡습니다. 편성표는 서버에 있고 화면은 단지 로비에 있어 둘이 어긋나는 순간을 아무도 못 보는 구조인데, 두 쪽을 다 가진 팀이라야 마지막 신호 시각과 단지별 규정을 한자리에 놓고 견줄 수 있습니다. 단지마다 다른 조용시간과 광고 몫 기준이 있어야 성립하는 판정이라 다른 회사 데이터로는 이 사슬이 만들어지지 않습니다. 다만 이 포트폴리오는 편성 대조 규칙과 운영 화면을 보여 줍니다. 홈넷 프로토콜이나 사이니지 하드웨어 자체의 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
주요업무에 단지 내 키오스크·POS·DID 연동 서비스 개발과 광고 및 공지 송출을 위한 CMS 설계가 나란히 적혀 있습니다. 편성을 만드는 쪽과 기기가 받아 트는 쪽이 같은 팀 안에 있어 둘 사이가 어긋나는 지점을 코드로 잡을 수 있습니다.
현장 기기의 배포·운영·모니터링을 위한 관리 기능 개발이 주요업무에 들어 있고, 운영 데이터를 근거로 한 장애 분석과 품질 개선도 따로 적혀 있습니다. 기기가 살아 있는지를 화면에 세우는 일이 곧 이 자리의 업무입니다.
특정 언어나 도메인 경험보다 문제를 빠르게 이해하고 필요한 기술을 학습해 실제 동작하는 결과물로 만들어내는 역량을 본다고 못박습니다. 추상적인 요구사항을 구체적인 설계로 정리할 수 있는지도 자격요건입니다.
오늘 편성한 줄마다 그 줄이 나갈 전광판이 아침 기준 시각까지 신호를 보냈는지, 소재가 그 화면 비율과 앱 버전에 맞는지, 단지가 정한 조용시간과 광고 몫을 넘지 않는지 맞춰 여섯 갈래로 나눠 보여 줍니다. 기기가 꺼진 시간은 편성 시각이 아니라 판정 기준 시각에서 마지막 신호를 빼 기기마다 하나로 내고, 광고 몫은 그 화면 하루치를 묶어 냅니다. 판정은 이 두 계산이 정하고, 모델은 걸린 이유를 담당자가 읽을 한 줄로 옮겨 적습니다.
여러 아파트 단지의 로비 전광판과 키오스크에 나갈 공지와 광고를 한자리에서 편성하는 운영 담당자 — 단지마다 전광판이 수십 대씩 흩어져 있어, 편성을 걸어도 실제로 떴는지는 입주민 항의 전화가 와야 안다
편성이 헛도는 이유는 담당자가 부주의해서가 아니라 편성표와 기기 상태와 단지 규정이 서로 다른 화면에 흩어져 있기 때문입니다. 셋을 한 판정으로 겹치면 담당자가 아침에 걸린 줄만 보고 손을 씁니다.
담당자가 편성표와 기기 신호 기록과 소재 목록과 단지 규정표를 올리면 오늘 편성 한 줄마다 판정이 붙고, 걸린 줄이 여섯 사유로 나뉘어 근거와 함께 뜹니다. 기기 신호는 내려받은 기록 파일로 시작하고, 기기에서 바로 받아 오는 연결은 2주차로 미룹니다.
기기 신호를 주기적으로 받아 판정을 다시 돌리고, 담당자가 손으로 판정한 48건과 맞춰 어디서 갈라지는지 리포트로 냅니다.
시간대가 겹친 줄은 조용시간 뒤로 옮기고 광고 몫을 넘은 줄은 다음 날로 미루는 안을 만들어 보여 주고, 담당자가 고르면 편성표가 그대로 바뀝니다.
담당자가 표 네 장을 올리면 오늘 편성 한 줄마다 판정이 붙고, 걸린 줄은 사유와 근거를 달고 옆에 남습니다.
화면 크기가 결측인 기기, 기한을 안 정한 상시 공지, 조용시간을 안 정한 단지를 섞은 편성 30종을 넣어도 화면이 죽지 않고 어느 줄이 왜 걸렸는지 그 자리에서 읽힙니다.
담당자가 손으로 판정한 편성 48건과 맞춰 일치율을 냅니다. 단, 정답과 데이터를 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
단지 12곳 전광판 214대 규모의 하루치 편성을 한 번에 판정하고 걸린 줄 수를 화면 맨 위에 띄웁니다.
위 수치는 더미데이터 자가채점이며 실제 송출 적중률은 에이치티비욘드의 실제 기기 신호와 단지 규정이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
일치율 수치는 제가 지어낸 편성표와 기기 신호에 제가 매긴 정답을 맞춰 본 자가채점입니다. 단지가 수백 곳으로 늘고 기기가 제각각 늙어 가는 현장에서는 다른 값이 나오므로, 면접에서는 데모가 도는 모습과 실제 신호를 받았을 때 무엇부터 확인할지를 말씀드립니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 결측 화면 크기와 기한 없는 소재와 조용시간을 안 정한 단지를 더미에 일부러 섞어 쉬운 문제만 푸는 함정을 피합니다.
신호가 끊겼다는 사실이 곧 화면이 꺼졌다는 뜻은 아닙니다. 통신만 끊기고 재생은 도는 경우가 있어 판정을 결론으로 읽지 않게 마지막 신호 시각을 항상 같이 보여 줍니다.
실제 도입에서는 단지마다 조용시간과 광고 몫 기준이 계약서로 따로 정해집니다. 규정이 늘어나면 조건을 코드에서 빼 규정표로 옮겨야 하며, 그것은 도입 단계 과제로 분리합니다.
이 포트폴리오는 편성 대조와 운영 화면을 보여 줍니다. 기기 원격 제어와 펌웨어 배포는 현장 장비가 있어야 다룰 수 있어 범위 밖에 둡니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
오늘 단지 전광판에 걸어 둔 공지와 광고 편성입니다. 판정의 대상이며 한 줄이 어느 기기로 나갈지 device_id 로 붙습니다. 같은 단지 같은 종류에 기기가 두 대인 경우를 담아 기기마다 규격 판정이 갈리는 자리가 드러나게 했고, 지난 소재를 그대로 다시 건 줄과 밤 시간대에 걸린 줄도 담았습니다. note 에는 그 줄의 사정만 적고 판정에 해당하는 말은 담지 않습니다.
| plan_id | complex_id | device_type | device_id | content_id | slot_start | slot_end | requested_by | note |
|---|---|---|---|---|---|---|---|---|
| PLAN-20260908-01 | CX-A101 | 로비DID | DV-A101-01 | CT-3001 | 2026-09-08T07:14:00 | 2026-09-08T07:44:00 | 관리사무소 | — |
| PLAN-20260908-02 | CX-B204 | 승강기사이니지 | DV-B204-01 | CT-3002 | 2026-09-08T22:30:00 | 2026-09-09T06:00:00 | 관리사무소 | — |
| PLAN-20260908-03 | CX-C307 | 정문키오스크 | DV-C307-01 | CT-3003 | 2026-09-08T12:00:00 | 2026-09-08T12:30:00 | 관리사무소 | — |
| PLAN-20260908-04 | CX-D412 | 로비DID | DV-D412-01 | CT-3004 | 2026-09-08T08:00:00 | 2026-09-08T20:00:00 | 광고팀 | — |
| PLAN-20260908-05 | CX-D412 | 로비DID | DV-D412-01 | CT-3005 | 2026-09-08T08:00:00 | 2026-09-08T20:00:00 | 광고팀 | — |
아래 JSON Schema 에 맞는 편성 행을 더 만들어 주세요. {"type":"object","properties":{"plan_id":{"type":"string"},"complex_id":{"type":"string"},"device_type":{"type":"string","enum":["로비DID","승강기사이니지","정문키오스크","단지POS"]},"device_id":{"type":"string"},"content_id":{"type":"string"},"slot_start":{"type":"string"},"slot_end":{"type":"string"},"requested_by":{"type":"string","enum":["관리사무소","광고팀","입주자대표회의"]},"note":{"type":["string","null"]}},"required":["plan_id","complex_id","device_type","device_id","content_id","slot_start","slot_end","requested_by"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) slot_start 와 slot_end 가 자정을 넘겨 다음 날로 이어지는 줄, (2) 같은 단지·같은 기기 종류에 광고가 여러 줄 몰린 하루, (3) 기한이 지난 소재를 다시 건 줄, (4) note 가 null 인 평범한 줄. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요. device_id 는 기기 신호 표에 있는 번호에서 고르되 그 기기의 complex_id·device_type 과 어긋나지 않게 두고, 한 단지에 같은 종류 기기가 두 대인 경우에는 편성을 두 기기에 나눠 거세요.{"type":"object","properties":{"plan_id":{"type":"string"},"complex_id":{"type":"string"},"device_type":{"type":"string","enum":["로비DID","승강기사이니지","정문키오스크","단지POS"]},"device_id":{"type":"string"},"content_id":{"type":"string"},"slot_start":{"type":"string"},"slot_end":{"type":"string"},"requested_by":{"type":"string","enum":["관리사무소","광고팀","입주자대표회의"]},"note":{"type":["string","null"]}},"required":["plan_id","complex_id","device_type","device_id","content_id","slot_start","slot_end","requested_by"]}단지에 설치된 전광판과 키오스크가 마지막으로 보낸 신호입니다. 앱 버전과 화면 크기를 함께 싣고, 설치 직후라 화면 크기가 결측인 기기와 하루 넘게 신호가 없는 기기를 담았습니다. note 에는 설치 사정만 적고 판정에 해당하는 말은 담지 않습니다.
| device_id | complex_id | device_type | last_seen_at | app_version | screen_size | install_spot | note |
|---|---|---|---|---|---|---|---|
| DV-A101-01 | CX-A101 | 로비DID | 2026-09-06T21:14:00 | 3.2.1 | 1920x1080 | 101동 로비 | — |
| DV-A101-02 | CX-A101 | 정문키오스크 | 2026-09-08T08:55:00 | 3.4.0 | 1080x1920 | 정문 경비실 옆 | — |
| DV-B204-01 | CX-B204 | 승강기사이니지 | 2026-09-08T08:58:00 | 3.4.0 | 1080x1920 | 204동 승강기 1호기 | — |
| DV-B204-02 | CX-B204 | 로비DID | 2026-09-08T08:59:00 | 3.4.0 | 1920x1080 | 204동 로비 | — |
| DV-C307-01 | CX-C307 | 정문키오스크 | 2026-09-08T08:57:00 | 3.4.0 | 1080x1920 | 정문 세로 거치대 | 세로로 세워 단 기기입니다 |
아래 JSON Schema 에 맞는 기기 신호 행을 더 만들어 주세요. {"type":"object","properties":{"device_id":{"type":"string"},"complex_id":{"type":"string"},"device_type":{"type":"string","enum":["로비DID","승강기사이니지","정문키오스크","단지POS"]},"last_seen_at":{"type":"string"},"app_version":{"type":"string"},"screen_size":{"type":["string","null"]},"install_spot":{"type":"string"},"note":{"type":["string","null"]}},"required":["device_id","complex_id","device_type","last_seen_at","app_version","install_spot"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) screen_size 가 null 인 설치 직후 기기, (2) last_seen_at 이 하루 넘게 지난 기기, (3) app_version 이 단지 기준보다 낮은 기기, (4) 세로로 세워 단 1080x1920 화면. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"device_id":{"type":"string"},"complex_id":{"type":"string"},"device_type":{"type":"string","enum":["로비DID","승강기사이니지","정문키오스크","단지POS"]},"last_seen_at":{"type":"string"},"app_version":{"type":"string"},"screen_size":{"type":["string","null"]},"install_spot":{"type":"string"},"note":{"type":["string","null"]}},"required":["device_id","complex_id","device_type","last_seen_at","app_version","install_spot"]}편성에 거는 공지와 광고 소재 목록입니다. 재생 길이와 해상도와 기한을 싣고, 기한을 정하지 않은 상시 공지와 해상도가 결측인 소재를 담았습니다. note 에는 소재를 받은 사정만 적고 판정에 해당하는 말은 담지 않습니다.
| content_id | title | media_type | duration_sec | resolution | expires_at | note |
|---|---|---|---|---|---|---|
| CT-3001 | 정기 소방점검 안내 | 공지 | 20 | 1920x1080 | 2026-09-30T23:59:00 | — |
| CT-3002 | 단수 안내 | 공지 | 15 | 1080x1920 | 2026-09-10T23:59:00 | — |
| CT-3003 | 가을 단지 행사 안내 | 공지 | 25 | 1920x1080 | 2026-09-20T23:59:00 | — |
| CT-3004 | 인근 마트 주말 할인 | 광고 | 30 | 1920x1080 | 2026-09-15T23:59:00 | — |
| CT-3005 | 단지 앞 치과 개원 | 광고 | 30 | 1920x1080 | 2026-09-18T23:59:00 | — |
아래 JSON Schema 에 맞는 소재 행을 더 만들어 주세요. {"type":"object","properties":{"content_id":{"type":"string"},"title":{"type":"string"},"media_type":{"type":"string","enum":["공지","광고"]},"duration_sec":{"type":"integer","minimum":5,"maximum":120},"resolution":{"type":["string","null"]},"expires_at":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["content_id","title","media_type","duration_sec"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) expires_at 이 null 인 상시 공지, (2) resolution 이 null 인 소재, (3) 세로 화면용 1080x1920 소재, (4) duration_sec 가 100초를 넘는 긴 광고. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"content_id":{"type":"string"},"title":{"type":"string"},"media_type":{"type":"string","enum":["공지","광고"]},"duration_sec":{"type":"integer","minimum":5,"maximum":120},"resolution":{"type":["string","null"]},"expires_at":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["content_id","title","media_type","duration_sec"]}단지와 기기 종류마다 정한 허용 매체와 최소 앱 버전과 조용시간과 광고 몫 상한입니다. 판정의 기준이 되는 표라 판정 결과를 담지 않습니다.
| complex_id | device_type | allowed_media_types | min_app_version | quiet_hours | max_ad_ratio | note |
|---|---|---|---|---|---|---|
| CX-A101 | 로비DID | ["공지","광고"] | 3.2.0 | 23:00~06:00 | 0.3 | — |
| CX-A101 | 정문키오스크 | ["공지","광고"] | 3.2.0 | — | 0.35 | 정문은 밤에도 켜 두기로 해서 조용시간을 안 정했습니다 |
| CX-B204 | 승강기사이니지 | ["공지"] | 3.3.0 | 22:00~07:00 | 0 | 승강기 안은 광고를 받지 않기로 한 단지입니다 |
| CX-B204 | 로비DID | ["공지","광고"] | 3.3.0 | 23:00~06:00 | 0.25 | — |
| CX-C307 | 정문키오스크 | ["공지","광고"] | 3.4.0 | 23:30~06:30 | 0.4 | — |
아래 JSON Schema 에 맞는 단지 규정 행을 더 만들어 주세요. {"type":"object","properties":{"complex_id":{"type":"string"},"device_type":{"type":"string","enum":["로비DID","승강기사이니지","정문키오스크","단지POS"]},"allowed_media_types":{"type":"array","items":{"type":"string"}},"min_app_version":{"type":"string"},"quiet_hours":{"type":["string","null"]},"max_ad_ratio":{"type":"number","minimum":0,"maximum":1},"note":{"type":["string","null"]}},"required":["complex_id","device_type","allowed_media_types","min_app_version","max_ad_ratio"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) quiet_hours 가 null 인 단지, (2) max_ad_ratio 가 0 이라 광고를 아예 안 받는 기기, (3) allowed_media_types 에 공지만 든 기기, (4) min_app_version 이 최신인 신축 단지. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"complex_id":{"type":"string"},"device_type":{"type":"string","enum":["로비DID","승강기사이니지","정문키오스크","단지POS"]},"allowed_media_types":{"type":"array","items":{"type":"string"}},"min_app_version":{"type":"string"},"quiet_hours":{"type":["string","null"]},"max_ad_ratio":{"type":"number","minimum":0,"maximum":1},"note":{"type":["string","null"]}},"required":["complex_id","device_type","allowed_media_types","min_app_version","max_ad_ratio"]}| TypeScript | 편성 판정 규칙과 화면을 같은 언어로 씁니다. 단지·기기·소재가 지나는 타입을 한 곳에 두어 규칙과 화면이 어긋나지 않게 합니다. |
| Node.js | 판정 규칙이 도는 서버입니다. 하루치 편성을 단지와 기기 종류로 묶어 광고 몫을 계산합니다. |
| React | 담당자가 보는 편성 화면입니다. 사유별 묶음과 근거 표를 한 화면에 둡니다. |
| PostgreSQL | 편성표와 소재 목록과 단지 규정표를 담습니다. 단지와 기기 종류를 함께 잡은 색인이 하루치 조회를 받칩니다. |
| Android | 현장 기기에서 도는 재생 앱 쪽 규격입니다. 앱 버전과 화면 크기를 신호에 실어 보내는 형식을 이 판정이 그대로 씁니다. |
| REST API | 기기가 주기적으로 신호를 올리고 편성을 내려받는 통로입니다. 신호가 끊긴 기기를 판정이 바로 집어냅니다. |
| Docker | 판정 서버와 신호 수집기를 같이 띄워 단지 여러 곳의 상황을 되풀이해 돌려 봅니다. |
| LLM | 걸린 이유를 담당자가 읽을 한 줄로 옮겨 적습니다. 어느 사유에 걸렸는지는 규칙이 정합니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 기기 신호를 직접 받아 판정을 되풀이 돌리는 단계로 넘어갑니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 걸린 편성을 고칠 안을 만들어 눌러 반영하는 단계로 확장합니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · plan_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
편성대로 나감편성대로 나감받을 전광판이 꺼져 있음전광판 꺼짐단지 규정 밖 시간대규정 밖 시간대전광판 규격이 안 맞음규격 불일치만료된 소재기한 지난 소재광고 비중 초과광고 몫 초과편성 한 줄에 대한 판정을 사유와 근거로 남기는 형식입니다. 사유는 규칙이 정하고 요약 문장만 모델이 씁니다.
{"type":"object","properties":{"check_id":{"type":"string"},"plan_id":{"type":"string"},"complex_id":{"type":"string"},"device_type":{"type":"string"},"device_id":{"type":"string","description":"그 편성이 나갈 기기입니다"},"verdict":{"type":"string","enum":["편성대로 나감","받을 전광판이 꺼져 있음","단지 규정 밖 시간대","전광판 규격이 안 맞음","만료된 소재","광고 비중 초과"]},"offline_hours":{"type":["number","null"],"minimum":0,"description":"판정 기준 시각(그날 아침 배치가 도는 2026-09-08T09:00)에서 그 기기의 device_heartbeat.last_seen_at 을 뺀 시간입니다. 편성이 아니라 기기마다 하나인 값이고, 한 시간을 넘으면 받을 전광판이 꺼져 있음으로 봅니다"},"ad_ratio":{"type":["number","null"],"minimum":0,"maximum":1,"description":"같은 complex_id·device_type 하루치 편성에서 media_type 이 광고인 줄의 duration_sec 합계를 전체 duration_sec 합계로 나눈 몫입니다. 판정 값과 상관없이 그 화면의 편성이 있으면 언제나 채웁니다"},"evidence":{"type":"array","items":{"type":"object","properties":{"dataset":{"type":"string"},"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{"type":["string","number","boolean","null"]}},"required":["dataset","field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["check_id","plan_id","verdict","evidence","summary"]}[
{
"check_id": "CHK-0007",
"plan_id": "PLAN-20260908-07",
"complex_id": "CX-B204",
"device_type": "로비DID",
"device_id": "DV-B204-02",
"verdict": "편성대로 나감",
"offline_hours": 0.02,
"ad_ratio": 0,
"evidence": [
{
"dataset": "device_heartbeat",
"field": "last_seen_at",
"value": "2026-09-08T08:59:00"
},
{
"dataset": "device_heartbeat",
"field": "device_id",
"value": "DV-B204-02"
},
{
"dataset": "content_asset",
"field": "resolution",
"value": "1920x1080"
},
{
"dataset": "device_heartbeat",
"field": "screen_size",
"value": "1920x1080"
},
{
"dataset": "complex_policy",
"field": "min_app_version",
"value": "3.3.0"
},
{
"dataset": "device_heartbeat",
"field": "app_version",
"value": "3.4.0"
},
{
"dataset": "complex_policy",
"field": "max_ad_ratio",
"value": 0.25
}
],
"summary": "204동 로비 전광판은 아침 9시 판정 기준으로 1분 전까지 신호를 보냈고 소재 해상도도 화면과 같습니다. 조용시간과 겹치지 않고 이 화면 하루 편성에 광고가 한 줄도 없어 재활용 배출 안내가 그대로 나갑니다.",
"hedge": null
},
{
"check_id": "CHK-0008",
"plan_id": "PLAN-20260908-08",
"complex_id": "CX-C307",
"device_type": "승강기사이니지",
"device_id": "DV-C307-02",
"verdict": "편성대로 나감",
"offline_hours": 0.07,
"ad_ratio": 0,
"evidence": [
{
"dataset": "device_heartbeat",
"field": "last_seen_at",
"value": "2026-09-08T08:56:00"
},
{
"dataset": "content_asset",
"field": "media_type",
"value": "공지"
},
{
"dataset": "content_asset",
"field": "expires_at",
"value": "2026-09-25T23:59:00"
},
{
"dataset": "device_heartbeat",
"field": "screen_size",
"value": "1080x1920"
},
{
"dataset": "content_asset",
"field": "resolution",
"value": "1080x1920"
},
{
"dataset": "complex_policy",
"field": "max_ad_ratio",
"value": 0
}
],
"summary": "307동 승강기 사이니지는 세로 화면이고 수영장 휴장 공지도 세로로 만들어져 비율이 맞습니다. 광고를 받지 않는 기기인데 이 화면 하루 편성이 공지뿐이라 그대로 나갑니다.",
"hedge": null
},
{
"check_id": "CHK-0001",
"plan_id": "PLAN-20260908-01",
"complex_id": "CX-A101",
"device_type": "로비DID",
"device_id": "DV-A101-01",
"verdict": "받을 전광판이 꺼져 있음",
"offline_hours": 35.77,
"ad_ratio": 0,
"evidence": [
{
"dataset": "device_heartbeat",
"field": "last_seen_at",
"value": "2026-09-06T21:14:00"
},
{
"dataset": "device_heartbeat",
"field": "device_id",
"value": "DV-A101-01"
},
{
"dataset": "device_heartbeat",
"field": "install_spot",
"value": "101동 로비"
},
{
"dataset": "broadcast_plan",
"field": "slot_start",
"value": "2026-09-08T07:14:00"
}
],
"summary": "101동 로비 전광판은 9월 6일 밤 9시 14분을 끝으로 신호가 없어 아침 9시 기준으로 35.77시간째 꺼져 있습니다. 소방점검 공지를 걸어도 뜨지 않으니 기기부터 살려야 합니다.",
"hedge": null
},
{
"check_id": "CHK-0002",
"plan_id": "PLAN-20260908-02",
"complex_id": "CX-B204",
"device_type": "승강기사이니지",
"device_id": "DV-B204-01",
"verdict": "단지 규정 밖 시간대",
"offline_hours": 0.03,
"ad_ratio": 0,
"evidence": [
{
"dataset": "broadcast_plan",
"field": "slot_start",
"value": "2026-09-08T22:30:00"
},
{
"dataset": "broadcast_plan",
"field": "slot_end",
"value": "2026-09-09T06:00:00"
},
{
"dataset": "complex_policy",
"field": "quiet_hours",
"value": "22:00~07:00"
},
{
"dataset": "content_asset",
"field": "title",
"value": "단수 안내"
},
{
"dataset": "device_heartbeat",
"field": "last_seen_at",
"value": "2026-09-08T08:58:00"
}
],
"summary": "단수 안내를 밤 10시 30분부터 새벽 6시까지 걸었는데 204동 승강기 사이니지의 조용시간이 밤 10시부터 아침 7시까지입니다. 기기는 살아 있으니 시간대를 아침 7시 뒤로 옮기거나 관리사무소가 예외를 받아야 나갑니다.",
"hedge": "단수처럼 급한 공지는 단지에서 예외로 열어 주는 경우가 있어 규정 밖이라는 판정이 곧 내리라는 뜻은 아닙니다."
},
{
"check_id": "CHK-0003",
"plan_id": "PLAN-20260908-03",
"complex_id": "CX-C307",
"device_type": "정문키오스크",
"device_id": "DV-C307-01",
"verdict": "전광판 규격이 안 맞음",
"offline_hours": 0.05,
"ad_ratio": 0,
"evidence": [
{
"dataset": "content_asset",
"field": "resolution",
"value": "1920x1080"
},
{
"dataset": "device_heartbeat",
"field": "screen_size",
"value": "1080x1920"
},
{
"dataset": "device_heartbeat",
"field": "install_spot",
"value": "정문 세로 거치대"
},
{
"dataset": "device_heartbeat",
"field": "app_version",
"value": "3.4.0"
},
{
"dataset": "complex_policy",
"field": "min_app_version",
"value": "3.4.0"
}
],
"summary": "가을 행사 안내 소재는 가로로 만들었는데 307동 정문 키오스크는 세로로 세워 단 화면입니다. 앱 버전은 단지 기준과 같아 걸리지 않았고, 그대로 내보내면 위아래가 검게 남으니 세로 소재를 따로 받아야 합니다.",
"hedge": null
},
{
"check_id": "CHK-0009",
"plan_id": "PLAN-20260908-09",
"complex_id": "CX-E520",
"device_type": "로비DID",
"device_id": "DV-E520-01",
"verdict": "전광판 규격이 안 맞음",
"offline_hours": 0.17,
"ad_ratio": 0,
"evidence": [
{
"dataset": "device_heartbeat",
"field": "app_version",
"value": "3.1.0"
},
{
"dataset": "complex_policy",
"field": "min_app_version",
"value": "3.4.0"
},
{
"dataset": "device_heartbeat",
"field": "device_id",
"value": "DV-E520-01"
},
{
"dataset": "content_asset",
"field": "resolution",
"value": "1920x1080"
},
{
"dataset": "device_heartbeat",
"field": "screen_size",
"value": "1920x1080"
}
],
"summary": "520동 로비 전광판의 앱이 3.1.0 이라 이 단지가 정한 3.4.0 에 못 미칩니다. 화면 비율은 소재와 같지만 앱을 올리기 전에는 택배 보관함 안내가 뜨지 않습니다.",
"hedge": "앱을 올린 뒤에만 송출하기로 한 단지라 규격 사유로 묶었습니다. 앱 배포 일정은 이 화면 밖에서 정해집니다."
},
{
"check_id": "CHK-0006",
"plan_id": "PLAN-20260908-06",
"complex_id": "CX-A101",
"device_type": "정문키오스크",
"device_id": "DV-A101-02",
"verdict": "만료된 소재",
"offline_hours": 0.08,
"ad_ratio": 0,
"evidence": [
{
"dataset": "content_asset",
"field": "expires_at",
"value": "2026-09-05T23:59:00"
},
{
"dataset": "broadcast_plan",
"field": "slot_end",
"value": "2026-09-08T10:00:00"
},
{
"dataset": "content_asset",
"field": "title",
"value": "추석 연휴 관리사무소 운영 안내"
},
{
"dataset": "content_asset",
"field": "resolution",
"value": "1080x1920"
},
{
"dataset": "device_heartbeat",
"field": "screen_size",
"value": "1080x1920"
}
],
"summary": "추석 연휴 안내 소재는 기한이 9월 5일에 끝났는데 오늘 9시 30분 편성에 그대로 걸려 있습니다. 세로 화면과 비율은 맞으니 규격이 아니라 날짜가 문제이고, 날짜가 지난 안내가 정문에 뜨면 입주민이 문의를 걸어 오므로 새 소재로 바꿔야 합니다.",
"hedge": null
},
{
"check_id": "CHK-0004",
"plan_id": "PLAN-20260908-04",
"complex_id": "CX-D412",
"device_type": "로비DID",
"device_id": "DV-D412-01",
"verdict": "광고 비중 초과",
"offline_hours": 0.01,
"ad_ratio": 0.469,
"evidence": [
{
"dataset": "content_asset",
"field": "duration_sec",
"value": 30
},
{
"dataset": "content_asset",
"field": "duration_sec",
"agg": "sum",
"value": 60
},
{
"dataset": "content_asset",
"field": "duration_sec",
"agg": "sum",
"value": 128
},
{
"dataset": "content_asset",
"field": "media_type",
"value": "광고"
},
{
"dataset": "complex_policy",
"field": "max_ad_ratio",
"value": 0.3
},
{
"dataset": "device_heartbeat",
"field": "device_id",
"value": "DV-D412-01"
},
{
"dataset": "broadcast_plan",
"field": "slot_start",
"value": "2026-09-08T08:00:00"
}
],
"summary": "412동 로비 전광판 하루 편성에서 광고가 60초, 전체가 128초라 광고 몫이 46.9%입니다. 이 단지가 정한 30%를 넘어 마트 할인과 치과 개원 가운데 하나를 다음 주로 미뤄야 합니다.",
"hedge": null
},
{
"check_id": "CHK-0012",
"plan_id": "PLAN-20260908-12",
"complex_id": "CX-D412",
"device_type": "로비DID",
"device_id": "DV-D412-02",
"verdict": "편성대로 나감",
"offline_hours": 0.01,
"ad_ratio": 0.469,
"evidence": [
{
"dataset": "device_heartbeat",
"field": "device_id",
"value": "DV-D412-02"
},
{
"dataset": "device_heartbeat",
"field": "screen_size",
"value": null
},
{
"dataset": "content_asset",
"field": "expires_at",
"value": null
},
{
"dataset": "content_asset",
"field": "media_type",
"value": "공지"
},
{
"dataset": "complex_policy",
"field": "max_ad_ratio",
"value": 0.3
}
],
"summary": "413동 로비 전광판은 신호가 살아 있고 택배 보관함 안내는 기한을 정하지 않은 상시 공지라 편성대로 나갑니다.",
"hedge": "이 기기는 설치 직후라 화면 크기를 아직 못 받아 소재와 비율을 견주지 못했습니다. 412동 로비 화면의 하루 광고 몫 46.9%는 단지 상한을 넘지만 그 사유는 같은 화면의 광고 줄에 붙고 이 공지 줄에는 붙지 않습니다."
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 enum 여섯 값(편성대로 나감·받을 전광판이 꺼져 있음·단지 규정 밖 시간대·전광판 규격이 안 맞음·만료된 소재·광고 비중 초과) 중 하나인지 확인합니다. 판정은 계산 규칙이 정하므로 모델이 다른 값을 쓰면 그 응답을 버립니다. 2. check_id·plan_id·verdict·evidence·summary 다섯 필드가 모두 채워졌는지 확인합니다. 3. offline_hours 가 0 이상인지, ad_ratio 가 0 이상 1 이하인지 범위를 확인합니다. 4. offline_hours 를 판정 기준 시각에서 그 편성 device_id 의 device_heartbeat.last_seen_at 을 뺀 시간으로 다시 계산해 값이 같은지 확인합니다. 판정 기준 시각은 그날 아침 배치가 도는 시각이며 이 데이터에서는 2026-09-08T09:00 입니다. 편성 시각이 아니라 기준 시각을 쓰므로 같은 기기에 걸린 편성은 아침 편성이든 밤 편성이든 같은 값을 갖고, 음수가 나오지 않습니다. 소수 둘째 자리에서 반올림하고, 한 시간을 넘으면 받을 전광판이 꺼져 있음입니다. 5. ad_ratio 를 같은 complex_id·device_type 하루치 편성에서 media_type 이 광고인 줄의 content_asset.duration_sec 합계를 전체 duration_sec 합계로 나눠 다시 계산하고, 소수 셋째 자리에서 반올림합니다. 판정 값과 상관없이 그 화면에 편성이 있으면 언제나 채웁니다. complex_policy.max_ad_ratio 를 넘으면 광고 비중 초과인데, 이 판정은 그 화면에 걸린 광고 줄에만 붙입니다. 같은 화면의 공지 줄은 몫을 계산하는 데는 들어가지만 판정은 받지 않습니다. 허용 매체가 공지뿐인 기기는 max_ad_ratio 가 0 이라 광고를 한 줄이라도 걸면 이 판정에 걸립니다. 6. evidence 의 field 가 broadcast_plan·device_heartbeat·content_asset·complex_policy 에 실재하는 컬럼 이름인지 대조합니다. 없는 이름을 인용하면 요약이 지어낸 문장이라는 뜻이라 규칙이 만든 기본 문장으로 되돌립니다. 7. agg 가 없는 evidence 값이 그 컬럼에 수록된 행 값과 같은지 대조합니다. 하루치를 더한 수치에는 agg 를 함께 적습니다. 8. summary 가 존댓말로 끝나는 한국어 문장인지 확인합니다. 9. content_asset.resolution 이나 device_heartbeat.screen_size 가 비어 있으면 비율을 견줄 수 없으므로 전광판 규격이 안 맞음으로 두지 않고, 견주지 못한 사실을 hedge 에 남깁니다. CT-3010 이 소재 해상도를 못 받은 경우이고 DV-D412-02 가 기기 화면 크기를 못 받은 경우이며, CT-3003 은 둘 다 있어 비율까지 견준 경우입니다. 앱 버전 비교는 화면 크기와 상관없이 그대로 합니다. 10. complex_policy.quiet_hours 가 비어 있으면 그 단지·기기는 시간대 조건을 건너뜁니다. CX-A101 정문키오스크가 조용시간을 정하지 않은 경우이고, CX-B204 승강기사이니지가 정해 둔 경우입니다. 11. 판정 순서는 받을 전광판이 꺼져 있음, 단지 규정 밖 시간대, 전광판 규격이 안 맞음, 만료된 소재, 광고 비중 초과, 편성대로 나감 순으로 앞에서 걸리면 뒤를 보지 않습니다. 그래서 기기가 꺼져 있으면 소재 기한은 보지 않습니다. 전광판 규격이 안 맞음은 세 가지를 한 자리에서 봅니다 — 소재 해상도와 기기 화면 크기의 가로세로 비율이 다를 때, device_heartbeat.app_version 이 그 단지 complex_policy.min_app_version 에 못 미칠 때, content_asset.media_type 이 complex_policy.allowed_media_types 밖일 때입니다. 셋 다 '이 기기로는 이 소재를 못 띄운다' 는 같은 뜻이라 사유를 하나로 묶고, 어느 쪽에서 걸렸는지는 evidence 와 요약이 말합니다. PLAN-20260908-09 가 앱 버전으로 걸린 경우입니다. 12. content_asset.expires_at 이 비어 있으면 기한 없는 상시 공지라 만료된 소재로 보지 않습니다. CT-3009 가 그 경우입니다. [평가 러너 · 2주차] 1. 운영 담당자가 손으로 판정한 편성 48건과 맞춰 일치율을 냅니다. 어긋난 건은 기기 상태에서 갈라졌는지 단지 규정에서 갈라졌는지 나눠 리포트에 답니다. [수동 검증 · 사람이 봅니다] 1. 규정 밖 시간대로 뜬 편성을 담당자가 실제로 옮기는지 표본 20건을 따라가 봅니다. 단수·정전 공지는 단지가 예외를 열어 주는 경우가 있습니다. 2. 신호가 끊긴 기준을 한 시간으로 둘지 세 시간으로 둘지 현장 통신 상태를 보고 정합니다.