최저가보다 먼저 판정하는 것 — 이게 정말 같은 물건인가
최저가 검색이 하는 일을 한 문장으로 적으면 이렇습니다. 여러 곳에 물어보고 제일 싼 값을 위에 올린다. 만들기 전에는 이게 전부라고 생각했습니다.
실제로 만들어 보니 어려운 부분은 다른 데 있었습니다. 값을 모으는 건 오히려 쉬웠고, 모인 값이 정말 같은 물건의 값인지 판정하는 일이 훨씬 어려웠습니다. 이 판정에 실패했을 때 우리 화면에 실제로 어떤 일이 벌어졌는지, 그리고 무엇을 고쳤는지 적어 두려고 합니다.
1등이 엉뚱한 물건이었다
반려견 처방 사료를 검색해 봤습니다. 로얄캐닌 유리너리 독. 결과 맨 위에 19,200원이
떴습니다. 다른 값들은 2만 원대 중후반이었으니, 눈에 딱 들어오는 최저가였습니다.
그런데 상품명을 보니 로얄캐닌 독 미니 다이제스티브 케어 1kg 이었습니다.
로얄캐닌도 맞고, 강아지용도 맞습니다. 그런데 유리너리가 없습니다. 검색한
사람이 찾던 건 비뇨기 처방 사료였고, 화면 1등은 소화 케어 사료였습니다.
왜 이게 통과했을까요. 당시 판정 규칙이 "검색어 단어 중 하나라도 겹치면 본품"
이었기 때문입니다. 로얄캐닌과 독, 두 단어가 겹쳤으니 통과였습니다. 정작 물건을
구별하는 단어인 유리너리가 없는데도요.
그래서 규칙을 바꿨습니다. 검색어의 단어가 전부 들어 있어야 본품으로 봅니다. 여러 단어로 검색하는 사람은 그 단어들을 모두 만족하는 물건을 찾는 것이니까요.
그런데 이번엔 너무 세게 걸렸다
규칙을 조이고 나서 무선이어폰을 검색해 봤습니다. 결과가 처참했습니다.
견적 180개 중 144개, 80%가 "검색어와 관련 없는 상품"으로 밀려 있었습니다.
밀려난 것들을 열어 보니 대부분 진짜 무선 이어폰이었습니다. 이유는 단순했습니다.
상품명이 ANC 노이즈캔슬링 무선 블루투스 이어폰 이었던 겁니다.
사람은 무선이어폰이라고 붙여서 칩니다. 판매처는 무선 블루투스 이어폰처럼
갈라서 적고 사이에 다른 말을 끼웁니다. "단어가 통째로 들어 있어야 한다"는 규칙에
이게 걸립니다. 한국어에서는 붙여 쓴 말과 갈라 쓴 말이 같은 말입니다.
그래서 붙여 쓴 검색어는 나눠서도 확인하도록 고쳤습니다. 표기 차이도 같이 흡수합니다 —
공백·하이픈·슬래시·점을 지우고 비교하기 때문에 유리너리 S/O와 유리너리SO,
유리너리 SO는 같은 말로 취급됩니다.
규칙을 조이면 진짜가 밀려나고, 풀면 가짜가 올라옵니다. 이 둘 사이를 계속 오가는 게 판정의 실체였습니다.
이름은 맞는데 물건이 아닌 것
전지적 독자 시점을 검색했을 때는 다른 종류의 실패가 나왔습니다. 최저가 11,000원,
상품명은 리유저블백 — 작품명이 통째로 들어간 에코백이었습니다. 단어가 전부
들어 있으니 규칙은 통과했고, 정작 사려던 책 자리를 굿즈가 차지했습니다.
같은 성격의 것들이 꽤 많습니다.
| 성격 | 예 |
|---|---|
| 부속·소모품 | 케이스, 필름, 스트랩, 충전기, 케이블, 거치대 |
| 굿즈·판촉물 | 에코백, 키링, 포스터, 엽서, 스티커, 텀블러, 달력 |
| 본품이 아닌 상태 | 중고, 리퍼브, 대여·렌탈, 한쪽, 낱개, 조립 |
| 가격이 확정되지 않는 표기 | 택일, 랜덤 |
마지막 줄이 특히 중요합니다. 택일이나 랜덤이 붙은 상품은 그 가격이 어떤 옵션의
가격인지 알 수 없습니다. 값이 있어도 비교에 쓸 수 없는 값입니다.
이 단어들이 검색어에 없는데 상품명에만 있으면 본품이 아니라고 봅니다. 다만 검색어에
있으면 정상 통과입니다 — 아이폰 케이스를 찾는 사람에게 케이스는 본품이니까요.
규칙으로 안 되는 건 의미로 판정한다
여기까지가 단어 규칙입니다. 빠르고, 돈이 들지 않고, 모든 검색에 적용됩니다. 하지만 단어만으로는 끝까지 갈 수 없습니다.
그래서 인세인 강도에서는 한 단계를 더 둡니다. 실질가가 싼 순으로 상위 20개를 언어모델(Claude Haiku)에게 보여 주고 "이게 검색한 물건과 같은 것인가"를 의미로 판정하게 합니다. 20개만 보는 이유는, 순위 상단을 지키는 데 필요한 구간이 거기까지라서 그렇습니다. 100개를 전부 판정하면 정확도는 조금 오르고 속도는 많이 떨어집니다.
이 단계는 단어 규칙의 실수도 되돌립니다. 상품명이 짧아서 억울하게 밀려난 견적을 "본품이다"라고 되살리는 쪽으로도 작동합니다.
중요한 건 그다음입니다. 이 판정이 실패해도 검색은 실패하지 않습니다. 언어모델 호출이 20초를 넘기거나 응답이 깨지면 조용히 단어 규칙 결과로 돌아갑니다. 판정 품질은 조금 낮아지지만 결과는 나옵니다. 더 좋은 장치를 붙일 때는, 그게 없을 때의 동작을 먼저 만들어 두는 게 순서라고 생각합니다.
판정이 끝나야 통계가 의미를 가진다
판정을 고치기 전, 사료 검색 화면에는 이런 숫자가 떠 있었습니다.
평균가 103,715원 · 최고가 대비 −95% · 절약 가능 385,000원
385,000원을 아낀다니 대단해 보이지만, 실은 아무 의미 없는 숫자였습니다. 결과에 0.1kg 1,900원부터 7.5kg 122,000원까지가 한 줄로 섞여 있었기 때문입니다. 소용량 샘플과 대용량 포대의 값을 평균 낸 것이니, 그 평균은 세상에 존재하지 않는 숫자입니다.
그래서 통계는 같은 규격끼리만 냅니다. 최저가 견적의 용량을 기준으로 삼고, 그 0.8~1.25배 범위에 드는 것만 평균·최고·최저 계산에 넣습니다. 표기 차이(1.5kg과 1500g)와 소량 오차를 흡수하는 폭입니다.
비교 가능한 게 3개도 안 되면 범위를 넓혀 전체로 계산합니다. 적은 표본으로 낸 정밀한 숫자가 큰 표본으로 낸 거친 숫자보다 위험하기 때문입니다. 이렇게 계산한 경우에는 어떤 기준으로 낸 숫자인지 화면에 밝힙니다.
걸러도 지우지 않는다
판정에서 밀려난 견적을 목록에서 빼 버리면 화면이 깔끔해집니다. 그렇게 하지 않았습니다.
의심 견적은 ⚠️ 확인 필요 표시를 달고 목록에 남습니다. 다만 순위와 통계에서는 빠집니다. 시세보다 크게 낮은 값(본품 시세 중앙값의 40% 미만)도 같은 방식으로 다룹니다 — 옵션가나 미끼가격일 수 있어 1등 자리를 주지 않지만, 감추지도 않습니다.
지우지 않는 이유는 두 가지입니다. 첫째, 우리 판정이 틀릴 수 있습니다. 사용자가 직접 보고 판단할 여지를 남겨야 합니다. 둘째, 걸러진 게 보이면 왜 걸렸는지 알 수 있습니다. 조용히 사라진 결과는 사용자가 검증할 방법이 없습니다.
이건 밥집 가격 제보에서 이상치를 계산에서만 빼고 기록은 남기는 것과 같은 원칙입니다. 판단은 하되, 판단의 근거를 감추지 않는 것.
우리가 고친 것들
| 겪은 문제 | 지금 하는 판정 |
|---|---|
| 단어 하나만 겹쳐도 통과 | 검색어 단어를 전부 만족해야 본품 |
| 붙여 쓴 말이 갈라 적힌 상품을 놓침 | 나눠서도 확인 · 표기 정규화 |
| 굿즈·부속이 본품 자리 차지 | 부속·굿즈·상태 단어 목록으로 제외 |
| 옵션 가격을 최저가로 오인 | 택일·랜덤 표기는 비교에서 제외 |
| 단어로 못 잡는 경우 | 인세인 강도에서 의미 판정 추가 |
| 서로 다른 규격을 평균 | 같은 용량대끼리만 통계 |
| 시세보다 비정상적으로 낮은 값 | 확인 필요 표시 · 순위 제외 |
최저가는 검색 결과가 아니라 판정 결과다
이 글에 적은 사례는 전부 우리 화면에서 실제로 벌어진 일입니다. 부끄러운 기록이지만 남기는 이유가 있습니다. 가격 비교에서 진짜 어려운 일이 어디에 있는지를 말하고 싶었습니다.
값을 많이 모으는 건 자랑거리가 아닙니다. 180개를 모아 놓고 1등이 엉뚱한 물건이면, 그건 0개를 모은 것보다 나쁩니다. 사용자는 그 숫자를 믿고 지갑을 열기 때문입니다.
그래서 판정은 계속 고쳐 나갈 일이라고 봅니다. 새로운 상품군을 검색해 보면 아직도 이상한 결과가 나오고, 그때마다 규칙을 하나씩 다듬습니다. 완성되는 종류의 일이 아니라는 걸 이제는 압니다.
미끼가격이 어떻게 작동하는지는 최저가의 함정에, 검색 강도를 어떻게 나눴는지는 느린 검색을 남겨 둔 이유에 적어 두었습니다. 직접 확인해 보고 싶다면 최저가 검색에서 평소 사는 물건을 검색해 보세요. 이상한 결과를 발견하면 그게 다음에 고칠 것입니다.