엑스포럼
경력근거: 기존 전시 관련 웹 시스템(TOPIC·EXSS)의 코드, 데이터와 배포 환경을 인수하고 개선
엑스포럼은 이 자리에 기존 전시 관련 웹 시스템(TOPIC·EXSS)의 코드와 데이터를 인수해 고치는 일과 사용자·관리자 웹 화면, 인증·권한 기능 개발을 함께 맡깁니다. 서울카페쇼처럼 참가업체가 수백 곳인 전시를 직접 주최하는 회사라, 부스를 어떤 순서로 내줬는지는 참가 신청서와 배정 내역이 한 시스템에 있어야 계산되고 그 순서를 업체에 설명하는 일도 주최사 몫입니다. 이 포트폴리오는 기준표로 부스배점을 계산하는 규칙과, 담당자와 팀장의 권한을 나눠 배치도 확정 전에 판정을 보는 Laravel 화면을 보여줍니다. TOPIC·EXSS 가 실제로 어떤 기능을 하는지는 공고에 없어 그 시스템을 흉내 내지 않고, 엑스포럼의 실제 배정 기준이나 전시 영업 실무에 대한 전문성은 주장하지 않습니다.
주요업무 첫 줄이 기존 전시 웹 시스템의 코드와 데이터를 인수해 개선하는 일이고, 전시 운영 시스템과 사내 제품 사이 데이터 연동이 뒤따릅니다. 전시 주최사의 웹 시스템이 다루는 대표 데이터가 참가 신청서와 부스 배정이라, 이 포트폴리오는 그 두 데이터를 맞대는 기능 하나를 처음부터 끝까지 만듭니다.
공고는 사용자·관리자 웹 화면과 API, 관계형 데이터베이스, 인증·권한 기능을 직접 개발한 경험을 요구합니다. 담당자는 판정을 보고 팀장만 배치도를 확정하는 두 역할 구조가 이 요구에 그대로 닿습니다.
자격요건은 PHP·Laravel 이나 Java·Spring 실무 경험과, 만든 기능의 테스트·배포·운영 이슈 해결 경험입니다. 판정 규칙을 PHPUnit 으로 고정하고 테스트를 통과한 커밋만 배포되게 CI 를 거는 흐름이 여기에 닿습니다.
엑스포럼은 서울카페쇼 같은 산업 전시를 주최하고 참가업체에 부스를 팝니다. 이 관리자 페이지는 한 전시의 참가 신청서와 부스 배정 내역, 부스배점 기준표를 받아 참가업체마다 네 판정 중 하나를 붙입니다. 같은 참가 유형(4부스 미만·4부스 이상·공식 스폰서) 안에서 신청 부스 수·참가 이력·신청 시각·계약금 납입 시각을 각각 순위 점수로 바꾸고, 기준표의 가중치를 곱해 더한 값이 부스배점입니다. 업체마다 첫 부스를 받은 시각을 골라 배점이 더 높은 업체보다 먼저 자리를 받았는지, 계약금을 내기 전에 받았는지, 받은 부스 수가 신청서와 같은지 봅니다. 전시사업팀 담당자는 배정 근거를 설명해야 할 업체를 이 판정으로 미리 알고, 팀장은 같은 화면을 보고 부스배치도를 확정할지 정합니다.
서울카페쇼 같은 전시의 참가업체를 맡아 부스를 배정하는 엑스포럼 전시사업팀 담당자와, 부스배치도를 확정하는 팀장
지금 담당자는 신청서 엑셀과 배치도 파일을 따로 열어 두고, 먼저 연락 온 업체나 자리를 강하게 요구한 업체부터 부스를 잡는 일이 생깁니다. 배정이 수백 건 쌓이면 어느 업체가 배점이 더 높은 업체를 앞질렀는지, 계약금이 들어오기 전에 자리를 받았는지 파일 두 개를 오가며 다시 짚기 어렵습니다. 배점을 기준표로 계산하고 첫 배정 시각을 업체 단위로 맞대면 항의가 오기 전에 근거가 서고, 팀장은 배치도를 확정하기 전에 손볼 업체를 먼저 압니다.
담당자가 참가 신청서, 부스 배정 내역, 부스배점 기준표 세 CSV 를 올리면 참가업체마다 네 판정 중 하나와 부스배점·참가 유형 안 순위·첫 배정 시각·계약금 납입 시각이 서는 Laravel 관리자 페이지입니다. 참가 유형별 순위 점수에 가중치를 곱해 배점을 내는 계산과, 업체마다 첫 배정 시각을 골라 배점이 더 높은 업체와 견주는 규칙이 이 주에 들어갑니다. 로그인은 담당자 한 역할로 두고, 부스를 아직 받지 않은 업체는 판정 없이 배정 대기 목록에 세웁니다.
참가 유형 구성이 다른 더미 전시 세 개(참가업체 120곳)를 더 만들어, 손으로 매긴 판정과 자동 판정이 갈린 업체를 판정별로 모읍니다. 담당자와 팀장 두 역할을 나눠, 판정이 걸린 업체가 남은 전시는 팀장의 배치도 확정 버튼이 사유를 띄우며 잠기고 누가 언제 확정했는지 기록이 남습니다. 판정 규칙 테스트를 CI 에 걸어 통과한 커밋만 배포됩니다.
배점이 낮은데 먼저 배정됨으로 걸린 건마다 Claude 가 두 업체의 항목별 순위 점수와 첫 배정 시각을 받아, 밀려난 업체에 보낼 배정 근거 안내문 초안 한 단락을 씁니다. 확정 직전 배정 내역을 스냅숏으로 남겨 두고, 확정 뒤 담당자가 부스를 옮기면 그 전시를 다시 판정해 새로 걸린 업체를 팀장에게 알립니다.
담당자가 「2026 서울카페쇼」 신청서와 배정 내역을 올리면 마루로스터스가 배점 65.0 으로 온결커피(100.0)보다 이틀 먼저 자리를 받아 배점 역전 묶음에 서고, 계약금 기록이 없는 한빛시럽과 납입 사흘 전에 자리를 받은 소담베이커리가 계약금 전 배정 묶음에 서는 과정을 시연합니다.
팀장이 업체 한 곳을 펼쳐 항목별 순위 점수와 가중치, 첫 배정 시각, 신청 부스 수와 받은 부스 수를 읽고 그 숫자로 배치도를 확정할지 정합니다.
납입 시각과 배정 시각이 같은 업체, 계약금이 null 이라 아직 부스를 받지 않은 업체, 6부스를 신청해 4부스만 받은 업체, 신청 부스 수와 참가 이력이 같은 업체들을 한 전시로 묶어도 업체마다 판정 하나가 서며, 시각 칸이 비거나 부스 번호가 빠진 배정 내역 파일 10종을 올리면 그 줄만 빠지고 나머지 업체 판정은 그대로 뜹니다.
2주차 더미 전시 세 개(참가업체 120곳)는 판정별로 자동 판정과 손 판정이 갈린 업체 수를 냅니다. 손 판정과 배점 기준표를 둘 다 제가 썼으므로 갈린 수가 0 이어도 배점 규칙이 문서대로 돈다는 뜻이고, 엑스포럼 담당자가 실제로 내릴 배정과 같다는 뜻은 아닙니다.
위 수치는 더미데이터 자가채점이며, 판정이 배정 항의를 실제로 줄이는지는 엑스포럼의 실제 참가 신청서와 배정 이력, 그 전시에 들어온 배정 항의 기록이 있어야 측정됩니다. 실제 전시의 신청서와 배정 내역을 CSV 로 내려받아 컬럼 이름만 맞추면, 기준표 가중치를 그대로 둔 채 그 전시의 배점과 판정을 다시 낼 수 있습니다.
더미 「2026 서울카페쇼」의 정답 판정은 제가 적었고, 부스를 실제로 배정해 본 전시사업팀이 보면 다르게 매길 업체가 있을 수 있습니다. 이 화면으로 보일 수 있는 것은 동점·계약금 null·같은 시각에서 배점과 판정이 문서대로 계산된다는 사실이고, 배정이 공정했다는 판단은 아닙니다.
배정 시각이 배점 순서와 그대로 맞물리는 전시라면 첫 배정 시각만 정렬해도 판정이 맞아, 순위 점수 계산이 틀려도 드러나지 않습니다. 배점 차이가 1.0 점뿐인 마루로스터스(65.0)와 바른컵(66.0), 계약금 기록 없이 자리를 받은 한빛시럽, 납입 시각과 배정 시각이 같은 바른컵, 6부스를 신청해 4부스만 받은 그라인드웍스를 한 전시에 둔 것은 그 계산을 시험하려는 것입니다.
부스 위치는 배점 순서만으로 정해지지 않습니다. 스폰서 계약, 같은 업종끼리 떼어 놓는 배치, 전기·급수 같은 설비 조건이 순서를 바꾸기도 하므로 이 판정은 설명이 필요한 업체를 세우는 데서 멈추고, 예외로 인정할지는 팀장이 정합니다.
가중치와 순위 점수 방식은 제가 정한 가정입니다. 참가 규모·신청 순서·참가 이력·참가비 납부 순으로 부스 위치를 정한다고 공지하는 방식은 국내 전시에서 흔하지만, 엑스포럼의 실제 기준은 이 포트폴리오가 알지 못하므로 기준표를 갈아 끼우는 자리만 둡니다.
이 포트폴리오는 기준표로 부스배점을 계산하고 배정 순서를 업체 단위로 맞대는 Laravel 관리자 페이지를 보여줍니다. 전시 영업과 부스 배치 실무에 대한 전문성은 주장하지 않습니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
엑스포럼 전시사업팀이 받는 참가 신청서입니다. 참가 유형은 4부스 미만·4부스 이상·공식 스폰서 셋이고, 계약금을 아직 내지 않은 업체는 deposit_paid_at 이 null 입니다. 계약금이 null 인데 자리를 받은 업체, 계약금이 null 이라 아직 부스가 없는 업체, 납입 시각이 배정 시각과 정확히 같은 업체를 일부러 담았습니다.
| company_id | company_name | tier | booth_count | applied_at | past_participations | deposit_paid_at |
|---|---|---|---|---|---|---|
| C-0019 | 하늘유업 | 공식 스폰서 | 4 | 2026-04-10T10:00:00+09:00 | 12 | 2026-04-30T15:20:00+09:00 |
| C-0027 | 대한커피머신 | 공식 스폰서 | 2 | 2026-04-15T14:30:00+09:00 | 8 | 2026-05-06T11:05:00+09:00 |
| C-0412 | 마루로스터스 | 4부스 미만 | 2 | 2026-06-03T10:12:00+09:00 | 1 | 2026-06-20T11:00:00+09:00 |
| C-0437 | 온결커피 | 4부스 미만 | 3 | 2026-05-28T09:05:00+09:00 | 4 | 2026-06-05T16:30:00+09:00 |
| C-0451 | 숲길티하우스 | 4부스 미만 | 1 | 2026-06-15T13:40:00+09:00 | 0 | 2026-07-02T10:20:00+09:00 |
아래 JSON Schema 에 맞는 전시 참가 신청서 행을 더 만들어 주세요. {"type":"object","properties":{"company_id":{"type":"string","description":"엑스포럼이 참가업체에 매긴 번호입니다."},"company_name":{"type":"string"},"tier":{"type":"string","enum":["4부스 미만","4부스 이상","공식 스폰서"],"description":"참가 유형입니다. 부스배점은 같은 참가 유형 안에서만 견줍니다."},"booth_count":{"type":"integer","minimum":1,"maximum":20,"description":"신청한 부스 수입니다."},"applied_at":{"type":"string","description":"참가 신청서를 낸 시각입니다. +09:00 오프셋이 붙은 ISO 8601 입니다."},"past_participations":{"type":"integer","minimum":0,"description":"같은 전시에 앞서 참가한 횟수입니다."},"deposit_paid_at":{"type":["string","null"],"description":"계약금이 입금된 시각입니다. 아직 내지 않았으면 null 입니다."}},"required":["company_id","company_name","tier","booth_count","applied_at","past_participations","deposit_paid_at"]} 한 전시에 참가 유형별로 4~40곳을 만들고, 4부스 미만은 booth_count 1~3, 4부스 이상은 4~12, 공식 스폰서는 2~8 로 둡니다. applied_at 은 전시 6개월 전부터 2개월 전 사이, deposit_paid_at 은 신청 뒤 1주~3개월 사이로 씁니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) deposit_paid_at 이 null 인 업체, (2) booth_count 와 past_participations 가 같은 업체 여럿(동점 순위), (3) 신청은 빨랐는데 계약금은 늦게 낸 업체, (4) past_participations 0 인 첫 참가 업체. 업체명은 커피·베이커리·티·카페 장비 업종의 가상 이름으로 쓰고, 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"company_id":{"type":"string","description":"엑스포럼이 참가업체에 매긴 번호입니다."},"company_name":{"type":"string"},"tier":{"type":"string","enum":["4부스 미만","4부스 이상","공식 스폰서"],"description":"참가 유형입니다. 부스배점은 같은 참가 유형 안에서만 견줍니다."},"booth_count":{"type":"integer","minimum":1,"maximum":20,"description":"신청한 부스 수입니다."},"applied_at":{"type":"string","description":"참가 신청서를 낸 시각입니다. +09:00 오프셋이 붙은 ISO 8601 입니다."},"past_participations":{"type":"integer","minimum":0,"description":"같은 전시에 앞서 참가한 횟수입니다."},"deposit_paid_at":{"type":["string","null"],"description":"계약금이 입금된 시각입니다. 아직 내지 않았으면 null 입니다."}},"required":["company_id","company_name","tier","booth_count","applied_at","past_participations","deposit_paid_at"]}부스배치도에 업체를 앉힌 기록입니다. 부스 하나에 한 줄이고, 같은 업체가 여러 부스를 받으면 줄이 여럿입니다. assigned_at 은 담당자가 그 부스를 업체에 잡은 시각입니다. 신청보다 적은 부스만 받은 업체와, 배점이 더 낮은데 먼저 자리를 받은 업체를 일부러 담았습니다.
| company_id | booth_no | assigned_at |
|---|---|---|
| C-0019 | A-101 | 2026-07-20T10:00:00+09:00 |
| C-0019 | A-102 | 2026-07-20T10:00:00+09:00 |
| C-0019 | A-103 | 2026-07-20T10:00:00+09:00 |
| C-0019 | A-104 | 2026-07-20T10:00:00+09:00 |
| C-0027 | A-110 | 2026-07-22T15:30:00+09:00 |
아래 JSON Schema 에 맞는 부스 배정 행을 더 만들어 주세요. {"type":"object","properties":{"company_id":{"type":"string","description":"부스를 받은 참가업체 번호입니다."},"booth_no":{"type":"string","description":"부스배치도의 부스 번호입니다. 부스 하나에 한 줄입니다."},"assigned_at":{"type":"string","description":"담당자가 그 부스를 업체에 잡은 시각입니다. +09:00 오프셋이 붙은 ISO 8601 입니다."}},"required":["company_id","booth_no","assigned_at"]} applications 의 company_id 마다 booth_count 만큼 booth_no 를 잡고, 같은 업체의 부스는 붙은 번호(B-230, B-231 …)로 같은 시각에 잡습니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 신청보다 적거나 많은 부스를 받은 업체, (2) 같은 참가 유형에서 배점이 낮은 업체가 높은 업체보다 먼저 받은 배정, (3) 계약금 납입 전 시각에 잡은 배정, (4) 계약금 납입 시각과 정확히 같은 시각에 잡은 배정, (5) 한 업체의 부스를 서로 다른 날에 나눠 잡은 배정. 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"company_id":{"type":"string","description":"부스를 받은 참가업체 번호입니다."},"booth_no":{"type":"string","description":"부스배치도의 부스 번호입니다. 부스 하나에 한 줄입니다."},"assigned_at":{"type":"string","description":"담당자가 그 부스를 업체에 잡은 시각입니다. +09:00 오프셋이 붙은 ISO 8601 입니다."}},"required":["company_id","booth_no","assigned_at"]}참가 유형마다 부스배점에 쓰는 네 항목의 가중치와 순위 방향을 적은 기준표입니다. 같은 참가 유형의 가중치를 더하면 1.0 이고, 값은 작성자가 정한 가정입니다. 팀장이 이 표를 바꾸면 판정이 다시 계산됩니다.
| tier | item | weight | direction |
|---|---|---|---|
| 4부스 미만 | booth_count | 0.2 | 클수록 앞 |
| 4부스 미만 | past_participations | 0.3 | 클수록 앞 |
| 4부스 미만 | applied_at | 0.25 | 이를수록 앞 |
| 4부스 미만 | deposit_paid_at | 0.25 | 이를수록 앞 |
| 4부스 이상 | booth_count | 0.3 | 클수록 앞 |
아래 JSON Schema 에 맞는 부스배점 기준표 행을 더 만들어 주세요. {"type":"object","properties":{"tier":{"type":"string","enum":["4부스 미만","4부스 이상","공식 스폰서"]},"item":{"type":"string","enum":["booth_count","past_participations","applied_at","deposit_paid_at"],"description":"부스배점에 쓰는 신청서 항목입니다."},"weight":{"type":"number","minimum":0,"maximum":1,"description":"그 항목의 가중치입니다. 같은 tier 의 네 항목을 더하면 1.0 입니다."},"direction":{"type":"string","enum":["클수록 앞","이를수록 앞"],"description":"순위를 세우는 방향입니다."}},"required":["tier","item","weight","direction"]} 참가 유형 하나에 item 네 줄을 모두 쓰고, 그 네 weight 를 더하면 정확히 1.0 이 되게 합니다. booth_count·past_participations 는 클수록 앞, applied_at·deposit_paid_at 은 이를수록 앞입니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) weight 가 0 인 항목, (2) 한 항목에 weight 가 0.5 넘게 몰린 참가 유형, (3) 네 항목이 0.25 로 똑같은 참가 유형. 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"tier":{"type":"string","enum":["4부스 미만","4부스 이상","공식 스폰서"]},"item":{"type":"string","enum":["booth_count","past_participations","applied_at","deposit_paid_at"],"description":"부스배점에 쓰는 신청서 항목입니다."},"weight":{"type":"number","minimum":0,"maximum":1,"description":"그 항목의 가중치입니다. 같은 tier 의 네 항목을 더하면 1.0 입니다."},"direction":{"type":"string","enum":["클수록 앞","이를수록 앞"],"description":"순위를 세우는 방향입니다."}},"required":["tier","item","weight","direction"]}| PHP | 순위 점수·가중합 계산과 판정 차례를 담은 규칙 엔진, CSV 업로드 처리의 언어입니다. |
| Laravel | 세 CSV 업로드 라우트와 판정 API, 판정 묶음·업체 펼침 화면을 Blade 로 그리는 관리자 페이지입니다. |
| 관계형 데이터베이스(MySQL) | 신청서·배정 내역·기준표 세 테이블과 판정 결과, 확정 기록을 담고 업체별 첫 배정 시각과 부스 수를 집계 쿼리로 셉니다. |
| API | 판정 목록과 업체 한 곳의 근거를 JSON 으로 내주는 엔드포인트입니다. 2주차 정답 대조 러너와 3주차 안내문 생성이 이 API 를 씁니다. |
| 인증·권한 | Laravel Gate 로 담당자와 팀장 두 역할을 나눠, 배치도 확정과 기준표 수정은 팀장만 하게 합니다. |
| 테스트(PHPUnit) | 동점 순위, 계약금이 null 인 업체, 납입 시각과 배정 시각이 같은 업체, 신청보다 적게 받은 업체를 단위 테스트로 고정합니다. |
| CI/CD | 판정 규칙 테스트가 통과한 커밋만 배포되게 파이프라인을 둡니다. |
| 로그·모니터링 | 업로드에서 빠진 줄과 그 사유, 판정 실행 시간을 로그로 남겨 담당자가 올린 파일이 왜 일부만 반영됐는지 되짚습니다. |
| 백업 | 배치도 확정 직전 배정 내역을 스냅숏으로 남겨 확정 뒤 바뀐 부스를 견줍니다. |
| Claude Sonnet | 3주차에 밀려난 업체에 보낼 배정 근거 안내문 초안을 씁니다. |
✗ fail → 담당자가 고른 연락 순서와 판정 묶음 순서가 다르면 계약금 전 배정을 부스 수 차이보다 앞에 둔 이유부터 다시 따지고, 두 사유가 겹친 업체는 상세 칸에 사유를 모두 적습니다.
✓ pass → 2주차는 담당자와 팀장 권한을 나누는 일과 더미 전시 세 개로 판정을 맞춰 보는 일부터 합니다.
✗ fail → 어긋난 업체만 모아 순위 점수 방식과 동점 처리부터 다시 봅니다.
✓ pass → 3주차 배정 근거 안내문과 확정 뒤 재판정으로 넘어갑니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · company_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
계약금 납입 전에 배정됨계약금 전 배정신청한 부스 수와 다르게 배정됨부스 수 차이배점이 낮은데 먼저 배정됨배점 역전배점 순서대로 배정됨순서대로 배정참가업체 한 곳이 부스배점 계산과 첫 배정 시각·계약금 납입 시각·부스 수 견줌을 거쳐 어떤 판정을 받는지 남기는 형식입니다. 배점과 판정은 기준표와 시각 비교로 정해지고, 모델은 3주차 안내문 초안에서만 쓰입니다.
{"type":"object","properties":{"company_id":{"type":"string"},"company_name":{"type":"string"},"tier":{"type":"string"},"verdict":{"type":"string","enum":["배점 순서대로 배정됨","배점이 낮은데 먼저 배정됨","계약금 납입 전에 배정됨","신청한 부스 수와 다르게 배정됨"]},"tier_size":{"type":"integer","minimum":1,"description":"같은 tier 로 신청한 업체 수입니다. 배정 여부와 관계없이 셉니다."},"item_points":{"type":"object","description":"항목마다 같은 tier 안 순위를 점수로 바꾼 값입니다. 점수 = 100 × (tier_size − 순위 + 1) / tier_size 이고, 동점은 평균 순위를, 계약금이 null 인 업체는 deposit_paid_at 순위 맨 뒤를 씁니다.","properties":{"booth_count":{"type":"number","minimum":0,"maximum":100},"past_participations":{"type":"number","minimum":0,"maximum":100},"applied_at":{"type":"number","minimum":0,"maximum":100},"deposit_paid_at":{"type":"number","minimum":0,"maximum":100}}},"booth_score":{"type":"number","minimum":0,"maximum":100,"description":"부스배점입니다. 같은 tier 의 scoring_rules.weight 를 item_points 에 곱해 더한 값으로, 소수 첫째 자리까지 씁니다."},"tier_rank":{"type":"integer","minimum":1,"description":"같은 tier 안에서 booth_score 가 높은 순서입니다."},"booth_count":{"type":"integer","minimum":1},"assigned_booth_count":{"type":"integer","minimum":1,"description":"booth_assignments 에서 이 company_id 의 booth_no 줄 수입니다."},"first_assigned_at":{"type":"string","description":"이 업체 booth_assignments.assigned_at 가운데 가장 이른 시각입니다."},"deposit_paid_at":{"type":["string","null"]},"pushed_company_id":{"type":["string","null"],"description":"같은 tier 에서 booth_score 가 더 높은데 first_assigned_at 이 이 업체보다 늦은 업체 가운데 배점이 가장 높은 곳입니다. 없으면 null 입니다."},"pushed_company_score":{"type":["number","null"]},"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":["company_id","company_name","tier","verdict","tier_size","item_points","booth_score","tier_rank","booth_count","assigned_booth_count","first_assigned_at","deposit_paid_at","evidence","summary"]}[
{
"company_id": "C-0412",
"company_name": "마루로스터스",
"tier": "4부스 미만",
"verdict": "배점이 낮은데 먼저 배정됨",
"tier_size": 5,
"item_points": {
"booth_count": 60,
"past_participations": 60,
"applied_at": 60,
"deposit_paid_at": 80
},
"booth_score": 65,
"tier_rank": 3,
"booth_count": 2,
"assigned_booth_count": 2,
"first_assigned_at": "2026-08-10T09:30:00+09:00",
"deposit_paid_at": "2026-06-20T11:00:00+09:00",
"pushed_company_id": "C-0437",
"pushed_company_score": 100,
"evidence": [
{
"field": "assigned_at",
"value": "2026-08-10T09:30:00+09:00"
},
{
"field": "company_id",
"value": "C-0437"
},
{
"field": "assigned_at",
"value": "2026-08-12T16:00:00+09:00"
},
{
"field": "deposit_paid_at",
"value": "2026-06-20T11:00:00+09:00"
}
],
"summary": "마루로스터스는 4부스 미만 다섯 곳 중 배점 65.0 으로 3위인데, 배점 100.0 인 온결커피보다 이틀 먼저 첫 부스를 받았습니다.",
"hedge": "배점 66.0 으로 2위인 바른컵도 마루로스터스보다 나흘 늦게 자리를 받았습니다. 두 업체 차이는 1.0 점이라, 밀려난 업체 칸에는 배점이 가장 높은 온결커피를 적었습니다."
},
{
"company_id": "C-0473",
"company_name": "한빛시럽",
"tier": "4부스 미만",
"verdict": "계약금 납입 전에 배정됨",
"tier_size": 5,
"item_points": {
"booth_count": 60,
"past_participations": 30,
"applied_at": 20,
"deposit_paid_at": 20
},
"booth_score": 31,
"tier_rank": 5,
"booth_count": 2,
"assigned_booth_count": 2,
"first_assigned_at": "2026-08-22T13:00:00+09:00",
"deposit_paid_at": null,
"pushed_company_id": null,
"pushed_company_score": null,
"evidence": [
{
"field": "deposit_paid_at",
"value": null
},
{
"field": "assigned_at",
"value": "2026-08-22T13:00:00+09:00"
},
{
"field": "booth_no",
"agg": "count",
"value": 2
}
],
"summary": "한빛시럽은 계약금 납입 기록이 없는데 8월 22일에 부스 두 개를 받았습니다.",
"hedge": "계약금이 비어 있어 납입 순위는 맨 뒤(20.0 점)로 세었습니다. 계약금이 들어오면 이 판정은 납입 시각과 배정 시각을 다시 견줍니다."
},
{
"company_id": "C-0588",
"company_name": "소담베이커리",
"tier": "4부스 이상",
"verdict": "계약금 납입 전에 배정됨",
"tier_size": 4,
"item_points": {
"booth_count": 50,
"past_participations": 25,
"applied_at": 25,
"deposit_paid_at": 50
},
"booth_score": 37.5,
"tier_rank": 4,
"booth_count": 4,
"assigned_booth_count": 4,
"first_assigned_at": "2026-09-12T17:20:00+09:00",
"deposit_paid_at": "2026-09-15T10:00:00+09:00",
"pushed_company_id": null,
"pushed_company_score": null,
"evidence": [
{
"field": "assigned_at",
"value": "2026-09-12T17:20:00+09:00"
},
{
"field": "deposit_paid_at",
"value": "2026-09-15T10:00:00+09:00"
}
],
"summary": "소담베이커리는 9월 15일에 계약금을 냈는데 사흘 앞선 9월 12일에 부스 네 개를 받았습니다.",
"hedge": null
},
{
"company_id": "C-0603",
"company_name": "그라인드웍스",
"tier": "4부스 이상",
"verdict": "신청한 부스 수와 다르게 배정됨",
"tier_size": 4,
"item_points": {
"booth_count": 100,
"past_participations": 100,
"applied_at": 100,
"deposit_paid_at": 100
},
"booth_score": 100,
"tier_rank": 1,
"booth_count": 6,
"assigned_booth_count": 4,
"first_assigned_at": "2026-08-05T11:00:00+09:00",
"deposit_paid_at": "2026-05-30T13:10:00+09:00",
"pushed_company_id": null,
"pushed_company_score": null,
"evidence": [
{
"field": "booth_count",
"value": 6
},
{
"field": "booth_no",
"agg": "count",
"value": 4
},
{
"field": "assigned_at",
"value": "2026-08-05T11:00:00+09:00"
}
],
"summary": "그라인드웍스는 6부스를 신청했는데 배치도에는 B-201 부터 B-204 까지 4부스만 잡혀 있습니다.",
"hedge": "배점은 4부스 이상 네 곳 중 1위라 배정 순서에는 문제가 없습니다. 나머지 2부스를 잡을지, 신청을 4부스로 줄일지는 담당자가 업체와 정합니다."
},
{
"company_id": "C-0468",
"company_name": "바른컵",
"tier": "4부스 미만",
"verdict": "배점 순서대로 배정됨",
"tier_size": 5,
"item_points": {
"booth_count": 60,
"past_participations": 80,
"applied_at": 80,
"deposit_paid_at": 40
},
"booth_score": 66,
"tier_rank": 2,
"booth_count": 2,
"assigned_booth_count": 2,
"first_assigned_at": "2026-08-14T14:00:00+09:00",
"deposit_paid_at": "2026-08-14T14:00:00+09:00",
"pushed_company_id": null,
"pushed_company_score": null,
"evidence": [
{
"field": "deposit_paid_at",
"value": "2026-08-14T14:00:00+09:00"
},
{
"field": "assigned_at",
"value": "2026-08-14T14:00:00+09:00"
},
{
"field": "booth_no",
"agg": "count",
"value": 2
}
],
"summary": "바른컵은 배점 66.0 으로 4부스 미만 2위이고, 계약금이 들어온 바로 그 시각에 신청한 2부스를 받았습니다.",
"hedge": "계약금 납입 시각과 배정 시각이 8월 14일 14시로 같아, 앞서지 않았으므로 납입 뒤 배정으로 셌습니다. 담당자가 입금 확인 직후 자리를 잡았다면 같은 분 단위로 찍히는 일이 흔합니다."
},
{
"company_id": "C-0019",
"company_name": "하늘유업",
"tier": "공식 스폰서",
"verdict": "배점 순서대로 배정됨",
"tier_size": 2,
"item_points": {
"booth_count": 100,
"past_participations": 100,
"applied_at": 100,
"deposit_paid_at": 100
},
"booth_score": 100,
"tier_rank": 1,
"booth_count": 4,
"assigned_booth_count": 4,
"first_assigned_at": "2026-07-20T10:00:00+09:00",
"deposit_paid_at": "2026-04-30T15:20:00+09:00",
"pushed_company_id": null,
"pushed_company_score": null,
"evidence": [
{
"field": "booth_count",
"value": 4
},
{
"field": "assigned_at",
"value": "2026-07-20T10:00:00+09:00"
},
{
"field": "weight",
"value": 0.4
}
],
"summary": "하늘유업은 공식 스폰서 두 곳 중 배점 100.0 으로 1위이고, 가장 먼저 신청한 4부스를 모두 받았습니다.",
"hedge": null
},
{
"company_id": "C-0611",
"company_name": "라떼팩토리",
"tier": "4부스 이상",
"verdict": "배점 순서대로 배정됨",
"tier_size": 4,
"item_points": {
"booth_count": 50,
"past_participations": 50,
"applied_at": 50,
"deposit_paid_at": 75
},
"booth_score": 55,
"tier_rank": 3,
"booth_count": 4,
"assigned_booth_count": 4,
"first_assigned_at": "2026-08-25T10:00:00+09:00",
"deposit_paid_at": "2026-06-25T09:40:00+09:00",
"pushed_company_id": null,
"pushed_company_score": null,
"evidence": [
{
"field": "assigned_at",
"value": "2026-08-25T10:00:00+09:00"
},
{
"field": "deposit_paid_at",
"value": "2026-06-25T09:40:00+09:00"
},
{
"field": "booth_no",
"agg": "count",
"value": 4
}
],
"summary": "라떼팩토리는 배점 55.0 으로 4부스 이상 3위이고, 배점이 더 높은 그라인드웍스보다 늦게, 배점이 낮은 소담베이커리보다 먼저 4부스를 받았습니다.",
"hedge": "배점 57.5 로 2위인 브루랩은 계약금이 비어 아직 부스를 받지 못했습니다. 배정이 없는 업체는 배정 대기 목록에 따로 두고 견줌 대상에서 빼므로, 라떼팩토리는 순서대로 배정된 것으로 봅니다."
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 배점 순서대로 배정됨·배점이 낮은데 먼저 배정됨·계약금 납입 전에 배정됨·신청한 부스 수와 다르게 배정됨 네 값 중 하나인지 확인합니다. 2. company_id·tier·verdict·tier_size·item_points·booth_score·tier_rank·booth_count·assigned_booth_count·first_assigned_at·deposit_paid_at·evidence·summary 필드가 빠짐없이 있는지 확인합니다. 3. item_points 의 네 값과 booth_score 가 0 이상 100 이하이고 tier_rank 가 tier_size 이하인지 확인합니다. 4. item_points 를 같은 tier 업체들의 순위로, booth_score 를 scoring_rules.weight × item_points 의 합으로, assigned_booth_count 를 booth_no 줄 수로, first_assigned_at 을 assigned_at 최솟값으로 다시 계산합니다. 5. deposit_paid_at 이 null 이거나 first_assigned_at 이 deposit_paid_at 보다 앞서면 verdict 는 계약금 납입 전에 배정됨입니다. 6. 앞 조건에 걸리지 않고 assigned_booth_count 가 booth_count 와 다르면 verdict 는 신청한 부스 수와 다르게 배정됨입니다. 7. 앞 두 조건에 걸리지 않고 pushed_company_id 가 null 이 아니면 verdict 는 배점이 낮은데 먼저 배정됨입니다. 어느 조건에도 걸리지 않으면 verdict 는 배점 순서대로 배정됨입니다. 8. evidence 의 field 이름이 applications·booth_assignments·scoring_rules 에 있는 컬럼인지 대조합니다. 9. evidence 의 value 가 수록된 행의 원문 값과 같은지 확인합니다. 여러 줄을 세어 얻은 수는 agg 를 적습니다. 10. summary 가 존댓말로 끝나는지 확인합니다. [판정 차례 · 위 규칙이 둘 이상 동시에 걸릴 때] 업체 하나에는 판정을 하나만 남깁니다. ① 계약금 납입 전에 배정됨 → ② 신청한 부스 수와 다르게 배정됨 → ③ 배점이 낮은데 먼저 배정됨 → ④ 배점 순서대로 배정됨. 계약금 없는 배정은 다른 업체 자리를 막고 있을 수 있어 가장 먼저 봅니다. 배점 견줌은 같은 tier 에서 부스를 한 개라도 받은 업체끼리만 하고, 부스를 아직 받지 않은 업체는 판정하지 않고 배정 대기 목록에 따로 세웁니다. [하드 케이스 · 판정이 갈리는 이유] 한빛시럽(C-0473)은 deposit_paid_at 이 null 인 결측 사례라 계약금 납입 전에 배정됨이고, 배점 계산에서는 납입 순위 맨 뒤로 셉니다. 브루랩(C-0625)도 계약금이 null 이지만 부스가 없어 판정 대상이 아니며, 배점 57.5 로 tier 순위 계산에는 들어갑니다. 바른컵(C-0468)은 납입 시각과 배정 시각이 같아 앞서지 않았으므로 정상으로 셉니다. 4부스 미만에서 booth_count 2 인 세 업체와 past_participations 0 인 두 업체는 동점이라 평균 순위를 씁니다. [평가 러너 · 2주차] 더미 전시 세 개 120곳에 손으로 매긴 정답 판정과 자동 판정을 대조해 판정별 일치율을 내고, 어긋난 업체는 따로 모아 순위 점수 방식과 동점 처리, 판정 차례를 다시 봅니다. [수동 검증 · 사람이 봅니다] 1. 배점이 낮은데 먼저 배정됨으로 걸린 업체가 스폰서 계약이나 설비 조건 같은 예외였는지 팀장이 판단합니다. 2. 신청한 부스 수와 다르게 배정된 업체에 남은 부스를 잡을지 신청을 줄일지는 담당자가 업체와 정합니다.