데이터 액터 4종을 마켓플레이스에 올렸습니다. 한국 부동산 실거래가를 정부 공개 API에서 받아 정규화해 주는 것들입니다. 사람이 아니라 기계가 고객인 축을 하나 실측해보려고 만든 자산입니다.
공개한 다음 날부터 추적 스크립트가 매일 한 줄씩 원장에 적습니다. 사흘 내내 같은 줄이었습니다.
{"listed": false, "u30": null, "gate": "D-27 · ?/5 · 색인 0/4"}listed: false. 스토어 검색에 안 나온다는 뜻입니다. 저는 이걸 "신규 액터라 색인이 늦나 보다"로 읽고 사흘을 기다렸습니다.
원장이 "안 나온다"고 할 때, 무엇이 안 나오는 겁니까?
나흘째에 직접 API를 두드려봤습니다. 스크립트가 쓰는 것과 같은 공개 엔드포인트입니다.
curl "https://api.apify.com/v2/store?username=ootssu&limit=50"응답이 이상했습니다.
{"data": {"total": 4, "items": []}}total은 4인데 items는 비어 있습니다. 없는 게 아니라, 세기는 세는데 안 주는 겁니다.
문서를 다시 읽으니 파라미터 하나가 있었습니다.
curl "https://api.apify.com/v2/store?username=ootssu&limit=50&includeUnrunnableActors=true"{"data": {"total": 4, "items": [
"korea-apartment-transaction-prices",
"korea-commercial-property-prices",
"korea-officetel-prices",
"korea-apartment-rent-prices"
]}}네 개 다 있었습니다. 처음부터 색인돼 있었던 겁니다.
문서에는 이렇게 적혀 있습니다. 기본 검색 결과는 "자동으로 실행하기에 안전하지 않은 액터" 를 제외한다 — 예를 들어 KYC를 통과하지 않은 개발자의 액터, 또는 사용자 기반이 크지 않은 전체 권한 액터. 제 액터는 제한 권한이었으니, 남는 사유는 계정 인증이었습니다.
제가 사흘 동안 고친 것들
여기가 부끄러운 부분입니다. listed: false를 "미게시"로 읽은 뒤 저는 이런 일을 했습니다.
- 콘솔의 공개 API 응답을 이미 잘 나가는 남의 액터와 필드 단위로 비교
- 제 액터엔
pictureUrl이 없고 남의 것엔 있다는 걸 발견 - 로고 4개를 디자인해서 만들고 업로드
- 그리고 다시 검색 → 여전히 0/4
로고는 있어야 하는 게 맞습니다. 그건 낭비가 아니었습니다. 다만 그게 원인이 아니었습니다. 저는 증상 옆에 있는 다른 결함을 고치고 있었고, 그동안 진짜 원인은 계정 설정 화면에 문장으로 떠 있었습니다.
Billing details and payment method not set
콘솔이 계속 알려주고 있었는데, 저는 API 응답만 노려보고 있었습니다.
여기서 당신이라면 어디를 보겠습니까
목록에 내 것이 안 보일 때, 선택지는 대개 셋입니다.
- 내 물건이 부실하다 — 메타데이터, 이미지, 설명을 채운다
- 아직 안 올라갔다 — 게시 절차를 다시 밟는다
- 올라갔는데 안 보여준다 — 필터·권한·인증 상태를 본다
저는 1번과 2번을 오가며 사흘을 썼습니다. 3번을 먼저 확인했다면 10분이었습니다. 그리고 3번을 확인하는 방법은 대개 "카운트와 목록이 일치하는가" 한 줄입니다. total과 len(items)가 어긋나면 그건 부재가 아니라 필터입니다.
계측이 두 상태를 한 값으로 뭉개고 있었습니다
진짜 결함은 API가 아니라 제 추적 스크립트에 있었습니다. 그 스크립트는 이렇게만 물었습니다. "검색 결과에 내 것이 있나?" 그리고 없으면 listed: false를 적었습니다.
그 한 값에 서로 완전히 다른 두 상태가 뭉개져 있었습니다.
- 게시가 안 됐다 → 할 일: 게시 절차를 밟는다
- 게시됐는데 필터에 가려졌다 → 할 일: 계정 인증을 끝낸다
원인이 다르면 할 일도 다릅니다. 같은 값으로 적으면 사흘 동안 엉뚱한 일을 하게 됩니다. 실제로 그랬습니다.
두 갈래로 나눴습니다.
found = store_rows() # 사람이 검색해서 발견 가능한 것
indexed = store_rows(include_hidden=True) # 색인엔 있으나 필터로 가려진 것 포함그리고 원장에 적히는 문장을 바꿨습니다.
gate: "미시작 · ?/5 · 노출 0/4(색인 4)"
note: "색인 4/4인데 기본 검색 노출 0 — 안전필터(KYC 미통과 추정)로 가려짐"이제 이 줄만 읽어도 다음에 뭘 해야 하는지 알 수 있습니다.
그리고 판정일을 옮겼습니다
부수 효과가 하나 더 있었습니다. 이 액터들에는 "공개 30일 뒤 타인 사용자 5명 이상"이라는 판정이 붙어 있었습니다. 공개일은 08-19, 판정일은 09-18.
그런데 방금 확인한 대로, 공개일부터 지금까지 이것들은 검색으로 발견될 수 없는 상태였습니다. 관찰창의 앞부분 전체가 "유입이 생길 수 없는 기간"입니다. 그대로 09-18에 사용자 수를 세면, 재는 것은 "기계가 이 데이터를 쓰는가"가 아니라 "인증이 언제 끝났는가"가 됩니다.
판정일을 고정 날짜에서 걷어내고 이렇게 바꿨습니다.
# 판정일은 공개일이 아니라 **노출 시작일 + 30일**이다.
# 노출 첫날은 원장에서 listed=true인 가장 오래된 행으로 자동 확정한다.
judge_on = _add_days(exposure_started_on(), OBSERVE_DAYS)노출이 시작되기 전에는 판정일이 null이고, 게이트 표시는 미시작입니다. 날짜 계산은 월말·연말·윤년 넘김까지 자체검증을 붙였습니다 — 판정일이 하루만 밀려도 그건 다른 실험이 되니까요.
자가진단 3줄
외부 플랫폼에 뭔가를 올려두고 "왜 안 보이지"를 겪고 있다면, 코드 고치기 전에 이 셋부터 보세요.
- 응답의 카운트와 목록 길이가 일치합니까?
total: 4, items: []는 부재가 아니라 필터입니다. - 그 목록 API에 "숨은 것도 포함" 파라미터가 있습니까? 문서의 파라미터 표를 끝까지 읽어보세요. 대개 기본값이 조용히 뭔가를 빼고 있습니다.
- 당신의 추적 스크립트는 "없다"와 "가려졌다"를 구분해 적습니까? 한 값으로 뭉개면, 원인이 다른데 같은 대응을 하게 됩니다.
솔직한 부분
이 오진의 비용은 사흘과 로고 4개였습니다. 로고는 어차피 필요했으니 실제 손실은 사흘입니다. 크지 않습니다.
무서운 건 그 사흘이 아니라, 제가 원장을 믿고 그 위에서 계속 판단하고 있었다는 점입니다. listed: false는 거짓말이 아니었습니다. 정확히 자기가 물어본 것에 정확히 답하고 있었습니다. 다만 그게 제가 알고 싶었던 질문이 아니었을 뿐입니다.
계측을 만들 때는 "이 값이 무엇을 말하는가"보다 "이 값이 서로 다른 두 상태를 같은 값으로 적고 있지 않은가" 를 먼저 물어야 한다는 걸 또 배웠습니다. 실패를 빈 데이터로 치환하지 말라는 규칙과 정확히 같은 병입니다 — 그때는 exit 0이 성공처럼 보였고, 이번엔 빈 배열이 부재처럼 보였습니다.
혹시 지금 "안 나온다"고 적고 있는 계측이 있나요? 그 값이 부재인지 필터인지, 오늘 curl 한 번으로 확인해보세요.