어베어(윈들리)
경력근거: 광고 세팅, 검수, 성과 분석처럼 반복되는 퍼포먼스 마케팅 업무를 AI Agent가
※ 실제 산출물이 아닌 AI 예측 시안입니다.
공고는 퍼비스를 광고 세팅·검수·성과 분석 같은 반복 업무를 AI Agent 가 돕고 실행하는 제품이라 적고, 정답이 정해지지 않아 고객을 만나 가설을 검증하는 중이라고 밝힙니다. 검수는 공고가 적은 세 가지 가운데 이미 제품에 들어 있는 자리이고, 어베어는 같은 조직에서 윈들리로 해외 구매대행 셀러의 상품 소싱과 주문 관리를 맡고 있어 소싱 상태와 소싱가를 광고 검수에 끌어올 수 있습니다. 매체 성과만 보는 도구는 품절된 상품에 광고가 도는 것을 끝내 못 잡으므로, 이 화면은 제품 둘이 한 조직에 있는 어베어에서만 성립합니다. 다만 이 포트폴리오는 두 제품의 데이터를 규칙으로 잇고 마진과 주문당 광고비를 계산하는 백엔드·화면 설계를 보여줍니다. 매체별 광고 API 운영이나 퍼포먼스 마케팅 실무의 전문성은 주장하지 않습니다. 모르는 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
공고는 퍼비스를 아직 정답이 정해진 제품이 아니라고 밝히고, 고객을 직접 만나 문제를 구체화하며 가설을 검증하는 중이라고 적습니다. 요구사항을 받아 구현하는 자리가 아니라 무엇을 만들지부터 같이 정하는 자리입니다.
TypeScript/NestJS 와 Next.js 로 백엔드와 프런트엔드를 함께 설계·개발하고, API·데이터 흐름·외부 시스템 연동을 하나의 제품 기능으로 연결하라고 적습니다. 기능 하나가 데이터부터 화면까지 끝나야 하는 구조입니다.
퍼비스 기능 개발이 주 업무이지만 Kotlin/Spring 기반 윈들리의 유지보수와 운영 이슈 대응에도 참여한다고 적습니다. 이커머스 운영 데이터와 광고 운영 데이터가 한 조직 안에 함께 있다는 뜻입니다.
윈들리에 올려 둔 상품의 소싱 상태·소싱가와 퍼비스가 돌리고 있는 광고 소재를 상품코드로 짝지어, 하루 한 번 소재마다 오늘의 집행안을 냅니다. 그대로 둘지, 내릴지, 문구를 다시 만들지, 예산을 내릴지, 셀러가 직접 볼지가 판정으로 붙고, 판정은 개당 마진과 주문당 광고비를 세는 계산 규칙이 정합니다. 모델은 셀러가 알림으로 받아 볼 문장을 씁니다. 어느 상품이 품절이라 어느 소재를 내렸는지를 셀러가 쓰는 말로 적는 일입니다.
윈들리로 해외 구매대행·위탁판매 상품을 올리고 퍼비스에 광고 운영을 맡긴 온라인 셀러 — 품절과 소싱가 변동은 상품 쪽에서 일어나는데 광고는 그와 따로 돌아, 어느 소재를 내려야 하는지 매번 스스로 찾아본다
광고비가 새는 이유는 소재 성과가 나빠서가 아니라 상품 쪽 변화가 광고 쪽에 도착하지 않아서입니다. 소싱 상태와 소싱가가 바뀐 시각을 소재 정보와 같은 표에 올려 두면, 내려야 할 소재가 셀러가 찾아보기 전에 목록으로 먼저 뜹니다.
셀러가 쓰는 화면 하나가 돕니다. 광고 소재 목록과 윈들리 상품 목록, 카테고리 기준표를 올리면 소재마다 판정이 붙고 판정별 묶음과 근거가 한 화면에 뜹니다. 1주차는 매체 광고 API 를 붙이지 않고 매체 관리 화면에서 내려받은 소재 성과 파일을 그대로 읽는 것으로 시작합니다.
셀러가 손으로 정한 60건으로 판정 일치율을 자동으로 내고, 어긋난 건이 상품 조건에서 갈라졌는지 광고비 계산에서 갈라졌는지 나눠 리포트로 냅니다. 2주차부터 판정 배치를 GCP 에서 돌리고 화면을 Vercel 로 배포해 셀러가 주소로 들어와 씁니다.
메타·구글 광고 API 로 소재와 지출을 받아 오고 윈들리에서 상품 상태를 내보내는 배치를 붙여 사람이 올리지 않아도 매일 아침 집행안이 만들어집니다. 판정이 바뀐 소재는 셀러에게 알림으로 나갑니다.
셀러가 소재 성과 파일과 상품 목록을 올리면 소재마다 오늘의 집행안이 판정별로 뜨고, 어느 값이 그 판정을 만들었는지 그 자리에서 펼쳐집니다.
배송비가 비어 있는 상품, 전환이 0이라 주문당 광고비를 낼 수 없는 소재, 상품 목록에 없는 상품코드를 섞은 입력 30종을 넣어도 화면이 죽지 않고 그 소재가 왜 계산 없이 남았는지 읽힙니다.
열 이름이 빠지거나 금액 칸이 비거나 인코딩이 깨진 파일 12종을 올려도 목록 대신 어느 줄이 왜 걸렸는지 이슈로 뜨고 나머지 소재는 그대로 판정됩니다.
셀러가 손으로 정한 60건으로 판정 일치율을 냅니다. 단, 소재·상품 자료와 정답을 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
위 수치는 더미데이터 자가채점이며 실제 광고비 절감폭은 어베어의 실 소재 지출과 윈들리 상품 이력이 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 면접에서는 데모 동작과 실검증 준비 상태로 솔직히 설명합니다.
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 배송비가 비어 있는 상품, 전환이 0이라 나눗셈이 성립하지 않는 극단 소재, 상품 목록에 없는 상품코드, 결측·악성·경계 케이스를 더미에 일부러 섞어 쉬운 문제만 푸는 함정을 피합니다.
주문당 광고비가 마진을 넘었다는 사실이 곧 그 소재가 나쁘다는 뜻은 아닙니다. 새로 올린 상품은 매체가 노출을 배우는 구간이라 며칠 동안 비싸게 도는 것이 정상이므로, 판정을 결론으로 읽지 않게 근거 값을 항상 같이 보여줍니다.
판정은 하루 한 번 대조한 시점의 상태를 말합니다. 소싱처 품절은 그 사이에도 일어나므로 대조 시각을 화면에 적고, 실시간에 가까워지려면 상품 상태 변경을 받아 오는 3주차 연동이 있어야 합니다.
실제 도입에서는 매체마다 지출·전환 집계 기준과 시차가 다르고, 위탁판매는 소싱처가 여럿이라 한 상품에 소싱가가 여러 개 붙습니다. 매체별 집계 기준과 소싱처 우선순위를 설정으로 빼는 것은 도입 단계 과제로 분리합니다.
광고 집행 자체를 바꾸는 일은 매체 계정 권한이 있어야 하므로 이 포트폴리오의 범위 밖입니다. 여기서는 무엇을 내리고 무엇을 고칠지 정한 목록을 내는 데까지만 다룹니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
퍼비스가 매체에 돌리고 있는 광고 소재 목록과 어제 하루치 지출·전환입니다. 판정 대상이며, 전환이 0이라 주문당 광고비를 낼 수 없는 극단 소재와 상품 목록에 없는 상품코드를 단 소재를 일부러 섞었습니다.
| creative_id | product_code | ad_channel | headline_price | daily_spend | conversions | state |
|---|---|---|---|---|---|---|
| CR-4120 | P-88301 | 메타 | 24,900 | 38,000 | 4 | 집행중 |
| CR-4133 | P-88312 | 구글 | 29,900 | 41,000 | 6 | 집행중 |
| CR-4141 | P-88320 | 메타 | 19,900 | 46,500 | 5 | 집행중 |
| CR-4152 | P-88334 | 구글 | 59,000 | 21,000 | 0 | 집행중 |
| CR-4164 | P-88341 | 메타 | 26,900 | 18,000 | 3 | 집행중 |
아래 JSON Schema 에 맞는 광고 소재 행을 더 만들어 주세요. {"type":"object","properties":{"creative_id":{"type":"string"},"product_code":{"type":"string"},"ad_channel":{"type":"string","enum":["메타","구글","틱톡","카카오모먼트","네이버GFA"]},"headline_price":{"type":"integer","minimum":0},"daily_spend":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"state":{"type":"string","enum":["집행중","일시중지","종료"]}},"required":["creative_id","product_code","ad_channel","headline_price","daily_spend","conversions","state"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) conversions 가 0인 소재(지출은 있는데 주문이 없는 건), (2) seller_products 에 없는 product_code 를 단 소재, (3) headline_price 가 현재 판매가와 어긋난 소재, (4) 같은 product_code 에 채널이 다른 소재 둘. 생성한 행은 반드시 위 스키마를 통과해야 하며, 인코딩이 깨지거나 헤더가 빠진 형식 깨뜨리기 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"creative_id":{"type":"string"},"product_code":{"type":"string"},"ad_channel":{"type":"string","enum":["메타","구글","틱톡","카카오모먼트","네이버GFA"]},"headline_price":{"type":"integer","minimum":0},"daily_spend":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"state":{"type":"string","enum":["집행중","일시중지","종료"]}},"required":["creative_id","product_code","ad_channel","headline_price","daily_spend","conversions","state"]}셀러가 윈들리에 올려 둔 상품의 소싱 상태와 가격입니다. 판정의 다른 쪽 축이며, 배송비가 아직 비어 있는 상품과 배송비가 0으로 극단에 닿은 무료배송 상품, 어젯밤 소싱가가 오른 상품을 함께 담았습니다.
| product_code | category | source_state | source_price_krw | sell_price | shipping_fee | updated_at |
|---|---|---|---|---|---|---|
| P-88301 | 생활잡화 | 품절 | 12,800 | 24,900 | 3,000 | 2026-09-05T20:14:00 |
| P-88312 | 패션의류 | 가격변동 | 21,400 | 34,500 | 3,000 | 2026-09-05T22:40:00 |
| P-88320 | 뷰티 | 판매중 | 10,600 | 19,900 | 2,500 | 2026-08-28T11:02:00 |
| P-88334 | 디지털기기 | 판매중 | 41,000 | 59,000 | 0 | 2026-08-30T09:15:00 |
| P-88341 | 주방용품 | 판매중 | 15,600 | 26,900 | — | 2026-09-02T14:33:00 |
아래 JSON Schema 에 맞는 상품 행을 더 만들어 주세요. {"type":"object","properties":{"product_code":{"type":"string"},"category":{"type":"string","enum":["패션의류","뷰티","생활잡화","주방용품","반려용품","유아용품","스포츠레저","디지털기기","인테리어소품","건강식품"]},"source_state":{"type":"string","enum":["판매중","품절","가격변동","판매중지"]},"source_price_krw":{"type":"integer","minimum":0},"sell_price":{"type":"integer","minimum":0},"shipping_fee":{"type":["integer","null"],"minimum":0},"updated_at":{"type":"string"}},"required":["product_code","category","source_state","source_price_krw","sell_price","updated_at"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) shipping_fee 가 null 인 상품(배송비를 아직 안 정한 건)과 0인 무료배송 상품, (2) source_state 가 품절·판매중지인 상품, (3) updated_at 이 최근 24시간 안이면서 source_price_krw 가 크게 오른 상품, (4) sell_price 에서 source_price_krw 와 shipping_fee 를 빼면 거의 남지 않는 상품. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식 깨뜨리기 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"product_code":{"type":"string"},"category":{"type":"string","enum":["패션의류","뷰티","생활잡화","주방용품","반려용품","유아용품","스포츠레저","디지털기기","인테리어소품","건강식품"]},"source_state":{"type":"string","enum":["판매중","품절","가격변동","판매중지"]},"source_price_krw":{"type":"integer","minimum":0},"sell_price":{"type":"integer","minimum":0},"shipping_fee":{"type":["integer","null"],"minimum":0},"updated_at":{"type":"string"}},"required":["product_code","category","source_state","source_price_krw","sell_price","updated_at"]}셀러가 카테고리마다 정해 둔 기준표입니다. 최소 마진율과 주문당 광고비 상한, 사람이 반드시 봐야 하는 카테고리 표시가 들어 있습니다. 판정에 쓰이는 기준은 소재나 상품에 붙어 있지 않고 전부 이 표에서 옵니다.
| category | min_margin_rate | max_cost_per_order | review_required |
|---|---|---|---|
| 패션의류 | 0.25 | 4,500 | false |
| 뷰티 | 0.3 | 5,000 | false |
| 생활잡화 | 0.22 | 3,000 | false |
| 주방용품 | 0.24 | 3,500 | false |
| 반려용품 | 0.28 | 4,000 | false |
아래 JSON Schema 에 맞는 카테고리 기준 행을 더 만들어 주세요. {"type":"object","properties":{"category":{"type":"string","enum":["패션의류","뷰티","생활잡화","주방용품","반려용품","유아용품","스포츠레저","디지털기기","인테리어소품","건강식품"]},"min_margin_rate":{"type":"number","minimum":0,"maximum":1},"max_cost_per_order":{"type":"integer","minimum":0},"review_required":{"type":"boolean"}},"required":["category","min_margin_rate","max_cost_per_order","review_required"]} 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) min_margin_rate 가 0.35 를 넘는 빡빡한 카테고리, (2) max_cost_per_order 가 1,000원대로 낮은 저가 카테고리, (3) review_required 가 참인 카테고리(광고 문구 심의가 있는 품목), (4) 마진율 기준은 낮은데 광고비 상한도 낮은 조합. 생성한 행은 반드시 위 스키마를 통과해야 하며, 형식 깨뜨리기 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"category":{"type":"string","enum":["패션의류","뷰티","생활잡화","주방용품","반려용품","유아용품","스포츠레저","디지털기기","인테리어소품","건강식품"]},"min_margin_rate":{"type":"number","minimum":0,"maximum":1},"max_cost_per_order":{"type":"integer","minimum":0},"review_required":{"type":"boolean"}},"required":["category","min_margin_rate","max_cost_per_order","review_required"]}| TypeScript | 판정 규칙부터 화면까지 한 언어로 씁니다. 원 단위 금액과 비율이 섞이지 않도록 마진·주문당 광고비 계산 함수마다 단위를 타입으로 박습니다. |
| NestJS | 소재·상품·기준표를 받아 판정을 돌리는 API 서버입니다. 규칙 실행은 이쪽 한 곳에만 둡니다. |
| Next.js | 셀러가 보는 오늘의 집행안 화면입니다. 판정별 묶음과 근거 표를 한 화면에 둡니다. |
| React | 판정 카드를 펼쳐 근거 값을 보여주는 화면 부분입니다. |
| PostgreSQL | 소재·상품·카테고리 기준표를 담는 저장소입니다. 상품코드에 건 인덱스가 하루치 대조 시간을 좌우합니다. |
| Supabase | 로컬과 배포가 같은 스키마를 쓰도록 마이그레이션을 맡고, 셀러 계정 로그인을 붙입니다. |
| GCP | 2주차부터 하루 한 번 도는 판정 배치를 여기서 돌립니다. |
| AWS | 윈들리 쪽 상품 데이터가 있는 자리입니다. 3주차에 그쪽에서 상품 상태를 내보내는 배치와 판정 배치를 잇습니다. |
| Kotlin | 윈들리에서 상품 상태와 소싱가를 내보내는 엔드포인트를 손대는 자리입니다. 3주차 연동이 이 코드를 건드립니다. |
| Spring | 그 엔드포인트가 올라가 있는 윈들리 서버 쪽 프레임워크입니다. 기존 코드를 읽고 최소한만 고칩니다. |
| LLM | 판정이 정해진 뒤 셀러에게 보낼 알림 문장을 씁니다. 판정 다섯 값에는 손대지 못합니다. |
| Claude | 알림 문장을 쓰는 기본 모델입니다. |
| OpenAI | 같은 입력을 넣어 문장 길이와 말투를 비교하는 대조 모델입니다. 어느 쪽이 셀러에게 읽히는지 2주차에 골라 고정합니다. |
| Langfuse | 알림 문장 호출과 응답을 남겨, 판정과 어긋난 문장이 나간 건을 찾아 프롬프트를 고칩니다. |
| Claude Code | 규칙 엔진 시험과 더미데이터 증강 스크립트를 쓰는 데 씁니다. 생성한 코드는 시험을 돌려 확인한 뒤 넣습니다. |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우려면 셀러가 손으로 정한 60건과 판정을 맞춰 보는 단계로 넘어갑니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 매체와 윈들리에서 자료를 자동으로 받아 오고 알림을 붙이는 단계로 확장합니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · creative_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
그대로 집행그대로 집행광고 내림광고 내림소재 다시 만듦소재 다시 만듦예산 낮춤예산 낮춤사람 검수로 넘김사람 검수광고 소재 하나에 대한 오늘의 집행 판정과 그 판정을 만든 숫자를 남기는 형식입니다. 판정 다섯 값은 계산 규칙이 정하고 알림 문장만 모델이 씁니다.
{"type":"object","properties":{"creative_id":{"type":"string"},"product_code":{"type":"string"},"verdict":{"type":"string","enum":["그대로 집행","광고 내림","소재 다시 만듦","예산 낮춤","사람 검수로 넘김"]},"cost_per_order":{"type":["integer","null"],"minimum":0,"description":"ad_creatives.daily_spend ÷ conversions 로 계산한 주문당 광고비입니다. conversions 가 0이면 비웁니다"},"margin_per_unit":{"type":["integer","null"],"description":"seller_products.sell_price 에서 source_price_krw 와 shipping_fee 를 뺀 개당 마진입니다. shipping_fee 가 비어 있으면 비웁니다"},"margin_rate":{"type":["number","null"],"minimum":0,"maximum":1,"description":"margin_per_unit ÷ sell_price 입니다"},"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":["creative_id","product_code","verdict","evidence","summary"]}[
{
"creative_id": "CR-4183",
"product_code": "P-88362",
"verdict": "그대로 집행",
"cost_per_order": 3080,
"margin_per_unit": 14900,
"margin_rate": 0.325,
"evidence": [
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 15400
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 5
},
{
"dataset": "seller_products",
"field": "sell_price",
"value": 45900
},
{
"dataset": "seller_products",
"field": "source_price_krw",
"value": 27500
},
{
"dataset": "seller_products",
"field": "shipping_fee",
"value": 3500
},
{
"dataset": "seller_products",
"field": "source_state",
"value": "판매중"
},
{
"dataset": "margin_rules",
"field": "max_cost_per_order",
"value": 5500
},
{
"dataset": "margin_rules",
"field": "min_margin_rate",
"value": 0.25
}
],
"summary": "소싱처가 그대로 팔고 있고 소재 문구 가격도 판매가와 같습니다. 주문 하나에 3,080원을 써서 개당 마진 14,900원 안에 들고 스포츠레저 상한 5,500원도 넘지 않아 그대로 둡니다.",
"hedge": null
},
{
"creative_id": "CR-4190",
"product_code": "P-88370",
"verdict": "그대로 집행",
"cost_per_order": 1800,
"margin_per_unit": 6000,
"margin_rate": 0.355,
"evidence": [
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 7200
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 4
},
{
"dataset": "seller_products",
"field": "sell_price",
"value": 16900
},
{
"dataset": "seller_products",
"field": "source_price_krw",
"value": 8400
},
{
"dataset": "seller_products",
"field": "shipping_fee",
"value": 2500
},
{
"dataset": "margin_rules",
"field": "max_cost_per_order",
"value": 4000
},
{
"dataset": "margin_rules",
"field": "min_margin_rate",
"value": 0.28
}
],
"summary": "주문당 광고비 1,800원이 개당 마진 6,000원과 반려용품 상한 4,000원 모두 밑입니다. 마진율 35.5%도 기준 28%를 넘어 손댈 곳이 없습니다.",
"hedge": null
},
{
"creative_id": "CR-4120",
"product_code": "P-88301",
"verdict": "광고 내림",
"cost_per_order": 9500,
"margin_per_unit": 9100,
"margin_rate": 0.365,
"evidence": [
{
"dataset": "seller_products",
"field": "source_state",
"value": "품절"
},
{
"dataset": "seller_products",
"field": "updated_at",
"value": "2026-09-05T20:14:00"
},
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 38000
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 4
}
],
"summary": "소싱처가 어제 저녁 8시 14분에 품절로 바뀌었는데 광고는 그대로 돌고 있습니다. 팔 수 없는 상품이라 마진이 남아도 소재를 먼저 내립니다.",
"hedge": null
},
{
"creative_id": "CR-4247",
"product_code": "P-99999",
"verdict": "광고 내림",
"cost_per_order": 2600,
"margin_per_unit": null,
"margin_rate": null,
"evidence": [
{
"dataset": "ad_creatives",
"field": "product_code",
"value": "P-99999"
},
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 5200
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 2
}
],
"summary": "상품 목록에 P-99999 가 없어 어떤 상품을 파는 소재인지 정해지지 않습니다. 판매가와 소싱가를 알 수 없어 마진을 낼 수 없으므로 내려 두고 상품 등록을 확인합니다.",
"hedge": "상품이 지워진 것인지 코드가 잘못 붙은 것인지는 셀러가 확인해야 갈라집니다."
},
{
"creative_id": "CR-4133",
"product_code": "P-88312",
"verdict": "소재 다시 만듦",
"cost_per_order": 6833,
"margin_per_unit": 10100,
"margin_rate": 0.293,
"evidence": [
{
"dataset": "ad_creatives",
"field": "headline_price",
"value": 29900
},
{
"dataset": "seller_products",
"field": "sell_price",
"value": 34500
},
{
"dataset": "seller_products",
"field": "source_price_krw",
"value": 21400
},
{
"dataset": "seller_products",
"field": "updated_at",
"value": "2026-09-05T22:40:00"
},
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 41000
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 6
}
],
"summary": "소재 문구는 29,900원인데 지금 판매가는 34,500원입니다. 어젯밤 10시 40분에 소싱가가 오르면서 판매가를 올린 뒤라, 문구를 고쳐 다시 올려야 클릭이 주문으로 이어집니다.",
"hedge": null
},
{
"creative_id": "CR-4141",
"product_code": "P-88320",
"verdict": "예산 낮춤",
"cost_per_order": 9300,
"margin_per_unit": 6800,
"margin_rate": 0.342,
"evidence": [
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 46500
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 5
},
{
"dataset": "seller_products",
"field": "sell_price",
"value": 19900
},
{
"dataset": "seller_products",
"field": "source_price_krw",
"value": 10600
},
{
"dataset": "seller_products",
"field": "shipping_fee",
"value": 2500
},
{
"dataset": "margin_rules",
"field": "max_cost_per_order",
"value": 5000
},
{
"dataset": "margin_rules",
"field": "min_margin_rate",
"value": 0.3
}
],
"summary": "주문 하나를 받는 데 9,300원을 쓰는데 그 주문이 남기는 마진은 6,800원입니다. 마진율 34.2% 자체는 뷰티 기준 30%를 넘으니 상품이 나쁜 것이 아니라 광고비가 큰 쪽이라, 하루 예산을 내려 상한 5,000원 안으로 들어오게 합니다.",
"hedge": null
},
{
"creative_id": "CR-4164",
"product_code": "P-88341",
"verdict": "예산 낮춤",
"cost_per_order": 6000,
"margin_per_unit": null,
"margin_rate": null,
"evidence": [
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 18000
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 3
},
{
"dataset": "seller_products",
"field": "shipping_fee",
"value": null
},
{
"dataset": "seller_products",
"field": "sell_price",
"value": 26900
},
{
"dataset": "margin_rules",
"field": "max_cost_per_order",
"value": 3500
}
],
"summary": "배송비가 아직 비어 있어 개당 마진을 낼 수 없습니다. 마진 대신 주방용품 상한 3,500원만 견줬는데 주문당 광고비가 6,000원이라 예산을 내립니다.",
"hedge": "배송비가 채워지면 마진과 다시 견줘 판정이 바뀔 수 있습니다."
},
{
"creative_id": "CR-4152",
"product_code": "P-88334",
"verdict": "사람 검수로 넘김",
"cost_per_order": null,
"margin_per_unit": 18000,
"margin_rate": 0.305,
"evidence": [
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 0
},
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 21000
},
{
"dataset": "seller_products",
"field": "sell_price",
"value": 59000
},
{
"dataset": "seller_products",
"field": "source_price_krw",
"value": 41000
},
{
"dataset": "seller_products",
"field": "shipping_fee",
"value": 0
}
],
"summary": "하루 21,000원을 쓰는 동안 주문이 하나도 없어 주문당 광고비를 낼 근거가 없습니다. 숫자로 자를 자리가 아니라 셀러가 소재를 보고 정할 자리로 넘깁니다.",
"hedge": null
},
{
"creative_id": "CR-4171",
"product_code": "P-88355",
"verdict": "사람 검수로 넘김",
"cost_per_order": 3000,
"margin_per_unit": 10800,
"margin_rate": 0.338,
"evidence": [
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 12000
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 4
},
{
"dataset": "seller_products",
"field": "source_state",
"value": "판매중"
},
{
"dataset": "seller_products",
"field": "sell_price",
"value": 32000
},
{
"dataset": "seller_products",
"field": "source_price_krw",
"value": 18200
},
{
"dataset": "seller_products",
"field": "shipping_fee",
"value": 3000
},
{
"dataset": "margin_rules",
"field": "max_cost_per_order",
"value": 4500
},
{
"dataset": "margin_rules",
"field": "min_margin_rate",
"value": 0.3
},
{
"dataset": "margin_rules",
"field": "review_required",
"value": true
}
],
"summary": "주문당 광고비 3,000원이 유아용품 상한 4,500원과 개당 마진 10,800원 밑이라 계산으로는 손댈 데가 없습니다. 유아용품은 기준표에서 검수를 켜 둔 카테고리라 계속 돌려도 되는지는 자동으로 정하지 않고 셀러에게 넘깁니다.",
"hedge": "검수가 필요한 카테고리인지는 셀러가 기준표에 켜 둔 값을 그대로 따르므로, 그 표가 오래되면 넘어오는 소재도 함께 어긋납니다."
},
{
"creative_id": "CR-4212",
"product_code": "P-88391",
"verdict": "사람 검수로 넘김",
"cost_per_order": 3666,
"margin_per_unit": 13100,
"margin_rate": 0.438,
"evidence": [
{
"dataset": "ad_creatives",
"field": "daily_spend",
"value": 11000
},
{
"dataset": "ad_creatives",
"field": "conversions",
"value": 3
},
{
"dataset": "seller_products",
"field": "source_state",
"value": "판매중"
},
{
"dataset": "seller_products",
"field": "sell_price",
"value": 29900
},
{
"dataset": "seller_products",
"field": "source_price_krw",
"value": 14300
},
{
"dataset": "seller_products",
"field": "shipping_fee",
"value": 2500
},
{
"dataset": "margin_rules",
"field": "max_cost_per_order",
"value": 5000
},
{
"dataset": "margin_rules",
"field": "min_margin_rate",
"value": 0.32
},
{
"dataset": "margin_rules",
"field": "review_required",
"value": true
}
],
"summary": "주문당 광고비 3,666원이 건강식품 상한 5,000원과 개당 마진 13,100원 안에 들어 숫자로는 손댈 곳이 없습니다. 건강식품은 문구 심의가 있어 기준표에서 검수를 켜 둔 카테고리라 계속 집행 여부를 셀러가 보고 정하게 넘깁니다.",
"hedge": null
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 enum 다섯 값(그대로 집행·광고 내림·소재 다시 만듦·예산 낮춤·사람 검수로 넘김) 중 하나인지 확인합니다. 판정은 계산 규칙이 정하므로 모델이 다른 값을 쓰면 그 응답을 버립니다. 2. creative_id·product_code·verdict·evidence·summary 다섯 필드가 모두 있는지 확인합니다. 3. margin_rate 가 0 이상 1 이하인지, cost_per_order 가 0 이상인지 범위를 확인합니다. 4. margin_per_unit 을 seller_products.sell_price 에서 source_price_krw 와 shipping_fee 를 뺀 값으로 다시 계산해 값이 같은지 확인합니다. 5. cost_per_order 를 ad_creatives.daily_spend ÷ conversions 로, margin_rate 를 margin_per_unit ÷ sell_price 로 다시 계산해 값이 같은지 확인합니다. 6. evidence 의 field 가 ad_creatives·seller_products·margin_rules 에 실재하는 컬럼 이름인지 대조합니다. 없는 이름을 인용하면 요약이 지어낸 문장이라는 뜻이라 규칙이 만든 기본 문장으로 되돌립니다. 7. agg 가 없는 evidence 값이 그 컬럼에 실제로 수록된 값과 같은지 대조합니다. 여러 소재에서 계산한 수치에는 agg 를 함께 적습니다. 8. summary 가 존댓말로 끝나는 한국어 문장인지 확인합니다. 9. shipping_fee 가 비어 있으면 margin_per_unit 과 margin_rate 를 비우고 margin_rules.max_cost_per_order 만으로 예산을 견줍니다. CR-4164 가 조건이 성립한 경우이고, CR-4141 이 배송비가 채워져 마진과도 견준 성립하지 않은 경우입니다. 10. 판정 순서는 광고 내림, 소재 다시 만듦, 사람 검수로 넘김(전환 0), 사람 검수로 넘김(마진율 미달), 예산 낮춤, 사람 검수로 넘김(검수 카테고리), 그대로 집행 순으로 앞에서 걸리면 뒤를 보지 않습니다. 상품 목록에 없는 상품코드는 소싱 상태를 볼 수 없으므로 광고 내림으로 먼저 걸립니다. 11. 사람 검수로 넘김은 세 자리에서 나옵니다. 첫째, conversions 가 0이면 daily_spend 를 나눌 수 없어 주문당 광고비가 서지 않으므로 숫자로 자르지 않고 넘깁니다(CR-4152). 둘째, 마진율이 margin_rules.min_margin_rate 에 못 미치면 광고비를 줄여도 팔수록 남는 것이 없어 예산 낮춤 대신 넘깁니다. margin_rate 가 비어 있으면 이 조건은 건너뜁니다. 셋째, margin_rules.review_required 가 켜진 카테고리에서 광고비·마진 계산에 걸리는 데가 없어 계속 집행으로 갈 소재를 넘깁니다(CR-4171·CR-4212). 12. review_required 는 그 값 하나로 판정을 정하지 않습니다. 검수 카테고리에서도 광고 내림·소재 다시 만듦·예산 낮춤은 계산이 그대로 정하고, 계산상 손댈 데가 없어 계속 돌리게 되는 소재만 사람에게 넘깁니다. 멈추거나 줄이는 결정은 자동으로 해도 손해가 늘지 않지만, 계속 돌려도 된다는 결정은 사람이 지는 편이 안전하기 때문입니다. 그래서 검수 카테고리인 CR-4171·CR-4212 는 계산 결과가 그대로 집행일 때만 사람 검수로 넘김이 됩니다. 13. 판정 대상은 ad_creatives.state 가 집행중인 소재입니다. 일시중지·종료 소재는 오늘의 집행안에서 빼고 판정하지 않습니다. ad_channel 은 판정에 쓰지 않고 카드에 매체를 적는 데만 씁니다. [평가 러너 · 2주차] 1. 셀러가 손으로 정한 60건으로 판정 일치율을 냅니다. 어긋난 건은 상품 쪽 조건에서 갈라졌는지 광고비 계산에서 갈라졌는지 나눠 리포트에 답니다. [수동 검증 · 사람이 봅니다] 1. 예산 낮춤으로 뜬 소재를 셀러가 실제로 내리는지 표본 20건을 따라가 봅니다. 신상품은 초기 학습 구간이라 주문당 광고비가 잠깐 높아도 그대로 두는 경우가 있습니다. 2. 소싱처 가격이 바뀐 시각과 셀러가 판매가를 고친 시각 사이의 간격이 실제로 얼마나 벌어지는지 셀러에게 듣고 판정 기준을 조정할지 정합니다.