소비자 수요 신호는 매일 쌓이는데, 왜 상품 판정에 쓰이지 못했나
우리 회사는 기업 대상 상담·자동화 서비스가 주력이지만, 소비자에게 직접 파는 상품 아이디어도 별도로 검토해 왔다. 이 검토를 위해 매일 소비자 관심 상품 신호를 자동으로 모으는 수집 시스템을 따로 두고 있었는데, 문제는 그 신호를 받아서 '이 상품을 팔아도 되는지' 판정하는 절차가 없었다는 점이다.
이 공백이 만드는 비용은 눈에 잘 안 띄지만 분명했다. 신호가 쌓여도 판정이 없으니, 그 신호가 '진짜 팔릴 만한 상품'인지 '그냥 스쳐 지나가는 관심'인지 구분할 방법이 없었다. 결국 담당자가 직접 하나씩 훑어보고 감으로 고르거나, 아니면 아무도 안 보고 그대로 묵히는 두 가지 중 하나로 흘러갔다. 판정 절차가 없으면 수집에 들인 노력이 상품으로 연결되지 못한 채 버려진다는 뜻이다.
실제로 코드에서 두 시스템을 잇는 연결부를 찾아봤더니 일치하는 줄이 0건이었다. 수집은 되는데 판정이 없는 상태 — 신호가 매일 쌓이기만 하고 상품으로 이어지지 못하는 구조였다. 판정이 없으니 상품 후보가 괜찮아 보이는지 아닌지는 결국 사람이 매번 눈으로 훑어봐야 했고, 그러다 보니 실제로 팔아볼 상품을 고르는 일 자체가 뒤로 밀리기만 했다.
이 공백을 메우는 작업에서 상품 후보 22건을 실제로 채점하기까지의 과정을 정리한다. 핵심은 '무엇을 만들었나'보다 '만들고 나서 돌려 보니 무엇이 나왔나'다 — 채점 결과가 예상과 크게 달랐고, 그 차이가 왜 생겼는지를 다시 거슬러 올라가는 편이 이 작업에서 가장 배울 점이 많았다.
이 문제를 은행 계좌로 비유하면 이해가 쉽다 — 매일 입금 내역은 찍히는데 잔액을 계산해 주는 장치가 없는 것과 같다. 입금 자체는 정상이니 겉보기엔 문제가 없어 보이지만, '지금 얼마가 남아 있는가'라는 가장 중요한 질문에는 아무도 답할 수 없다.
판정 배선을 실측했더니: 도소매 순이익률 6.38%, 광고 최저가 780원부터
판정 절차를 만들기 전에, 상품 하나가 '팔아도 남는 장사인지'를 가르는 데 필요한 값부터 실측했다. 여기서 위탁판매란 재고를 직접 사서 쌓아 두지 않고, 공급자에게서 상품을 받아 주문이 들어올 때마다 넘기는 방식으로 파는 형태를 말한다. 이 방식으로 상품을 파는 업종 전체의 평균 이익률과, 실제 검색광고 시장에서 클릭 1회를 사는 데 드는 최저 비용, 그리고 우리가 검토하는 상품 후보 각각의 실제 판매 단가 — 셋을 각각 다른 출처에서 확인했다.
여기서 '검색광고 최저 입찰가'란, 소비자가 네이버 같은 검색 서비스에서 특정 상품을 검색했을 때 내 광고를 화면에 노출시키기 위해 최소한으로 걸어야 하는 입찰 금액을 말한다. 이 금액은 카테고리 마다 크게 다르다 — 경쟁이 적은 카테고리는 780원처럼 낮고, 경쟁이 치열한 카테고리는 13,280원 까지도 올라간다. 광고를 아예 안 태우면 노출 자체가 안 되는 시장이 많기 때문에, 이 최저가는 '광고 없이 팔 수 있는가'가 아니라 '광고를 태워야 팔리는 시장에서 최소한 얼마가 드는가'를 보여주는 값이다.

이 세 값이 판정의 재료다. 순소득률은 '이 업종에서 일반적으로 얼마가 남는가', 최저 입찰가는 '광고를 태우려면 클릭 1회에 최소 얼마가 드는가', 소매 단가는 '실제 이 상품이 얼마에 팔리는가'를 각각 나타낸다. 세 값을 겹쳐 보면 상품 하나가 광고를 태워도 남는 장사인지 아닌지가 계산으로 나온다 — 사람이 감으로 '이 상품 괜찮아 보인다'고 판단하던 자리를 숫자로 바꾸는 일이 이번 작업의 목표였다.
다만 세 값 중 하나는 시작부터 구멍이 있었다. 상품 후보 22건 중 소매 단가를 실제로 관측할 수 있었던 것은 3건뿐이었고, 나머지 19건은 이 값이 비어 있는 채로 판정에 들어갔다. 이 공백이 나중에 채점 결과를 해석할 때 중요한 단서가 된다.
가설: 목록 파일을 하나 더 만들면 될 줄 알았다
처음 세운 설계는 단순했다. 기업 대상 상품 후보를 관리하는 기존 목록 파일이 있으니, 소비자 대상 상품 후보는 별도의 새 목록 파일로 만들면 된다는 안이었다. 기존 목록을 건드리지 않고 새 목록에만 소비자용 채점표를 얹으면 충돌 없이 깔끔하게 분리될 것으로 봤다. 서로 다른 종류의 데이터를 서로 다른 파일에 담는 편이 언뜻 자연스러운 선택처럼 보였다.
기존 목록에 이미 쌓여 있던 기록 129줄에는 소비자용인지 기업용인지 구분하는 값이 없었기 때문에, 이 129줄에 구분 값을 붙여 주는 별도의 변환 작업을 한 차례 실행하는 방안도 함께 검토했다. 한 번 스크립트를 돌려서 129줄 전체에 '기업용' 표시를 붙이고 나면, 그 뒤로는 새 기록에만 신경 쓰면 된다는 계산이었다.
이 안이 매력적으로 보인 이유는 두 가지였다. 기존 목록의 채점 로직을 전혀 건드리지 않아도 되고, 소비자용 채점표를 자유롭게 새로 설계할 수 있다는 점이었다. 실제로 손을 대기 시작하고 나서야 이 안이 눈에 안 보이는 곳에서 얼마나 많은 부분을 함께 두 벌로 만드는지가 드러났다.
목록 파일을 쪼개면 무엇이 깨지는가
새 목록 파일 안이 걸린 지점은 두 가지였다. 첫째, 상품 후보 목록을 읽어서 화면에 보여주거나 이상 상태를 알려주는 장치가 이미 네 곳에 배선돼 있었다. 목록을 둘로 쪼개면 이 네 곳 전부가 '어느 목록을 볼지'를 다시 판단해야 하고, 그중 하나만 갱신을 놓쳐도 화면과 실제 상태가 어긋난다. 같은 정보를 두 군데서 따로 관리하면 두 값이 서서히 벌어지는 것은 시간 문제일 뿐이다.
둘째, 기존 129줄에 구분 값을 붙이는 변환 작업 방식이 놓치는 경우가 있었다. 이 회사는 여러 대의 컴퓨터에서 같은 목록 파일을 나눠 쓰는데, 아직 최신 상태로 동기화되지 않은 컴퓨터에 남아 있는 옛 기록은 이 변환 작업이 지나간 뒤에 뒤늦게 나타난다. 한 번 실행하고 끝나는 변환은 그 뒤늦게 나타나는 기록까지는 잡지 못하고, 그 기록은 구분 값이 없는 채로 영영 남는다.
실제로 걱정한 시나리오는 이런 식이다. 화면에 상품 후보를 보여주는 장치는 새 목록 파일을 보도록 고쳤는데, 이상 상태를 알려주는 감시 장치는 옛 목록 파일을 계속 보고 있다면 어떻게 될까. 화면 에는 새 후보가 보이는데 감시 장치는 '변화 없음'이라고 알려 준다 — 둘 다 틀린 말은 아니지만, 서로 다른 사실을 말하고 있어 어느 쪽을 믿어야 할지 알 수 없게 된다. 네 곳을 전부 빠짐없이 고쳤는지 매번 확인하는 비용은, 목록을 쪼개지 않았다면 애초에 들지 않았을 비용이다.
같은 목록에 구분 값 하나만 추가하는 편이 맞았다
최종 설계는 목록을 쪼개지 않고, 기존 목록에 소비자용인지 기업용인지 구분하는 값 하나만 추가하는 것이었다. 채점표의 항목 이름과 만점(18점)은 공유하되, 각 항목이 실제로 무엇을 재는지는 구분 값에 따라 다르게 해석하도록 했다. 만점을 바꾸지 않은 이유는 만점을 바꾸면 기존에 쌓여 있던 기업용 기록 122건과 점수를 더 이상 나란히 비교할 수 없기 때문이다 — 같은 자로 재야 지난 판정과 이번 판정을 같이 놓고 볼 수 있다.
구분 값을 붙이는 방식도 바꿨다. 129줄을 고쳐 쓰는 변환 작업 대신, 구분 값이 없는 기존 기록은 읽는 순간 자동으로 '기업용'으로 해석하게 만들었다. 이렇게 하면 아직 동기화되지 않은 컴퓨터의 옛 기록도 나중에 읽힐 때 자동으로 맞게 해석된다 — 변환 작업 한 번으로는 잡지 못했던 경우까지 구조적으로 해결됐다. 목록을 다시 조회해 봐도 기업용 기록은 여전히 129건, 재채점이나 번호 재부여는 0건이었다. 데이터를 고쳐 쓰는 대신 읽는 쪽의 기본값을 바꾸는 이 선택이, 나중에 몇 대의 컴퓨터가 있는지 전부 파악하지 않아도 되게 해 준다.
세 번째로, 채점표 안의 '자동 채점 항목'을 사람이 손으로 입력하지 못하게 막았다. 이 항목 중 하나는 위탁판매 형태인지를 나타내는데, 이 회사는 예전에 위탁판매 상품 하나를 사업성이 부족하다고 판단해 이미 접은 적이 있다. 그 판단이 사람 손 채점으로 다시 살아나는 경로를 막기 위해, 이 항목은 자유 입력이 아니라 신청 정보에서 자동으로 계산되도록 했다 — 한 번 접은 사업 형태를 담당자가 좋게 보인다는 이유만으로 다시 통과시키는 일을 막는 장치다.
이 규칙이 실제로 작동하는지도 확인했다. 위탁판매 형태인 상품 후보 하나를 일부러 목록에 올려 채점을 돌리자, 이 항목의 점수가 강제로 1점에서 0점으로 내려가는 것을 확인했다. 다만 이 검증에는 한계가 있다 — 위탁판매 형태의 후보가 실제로 0건이면 이 규칙은 아무 것도 걸러내지 못한 채 공허하게 통과한다. 규칙이 있다는 사실과 그 규칙이 실제로 무언가를 걸러낸다는 사실은 다르고, 이번에는 후자까지 직접 확인했다.
세 결정을 관통하는 기준은 하나였다 — 같은 정보를 두 곳에 두지 않는다. 목록을 쪼개지 않은 것도, 변환 작업 대신 읽는 쪽 기본값을 바꾼 것도, 사람이 손으로 점수를 못 매기게 막은 것도 전부 '나중에 두 값이 서로 다른 말을 하게 되는 경로'를 하나씩 없앤 결정이었다.
이 판단 기준은 이후에도 그대로 재사용할 수 있다. 다음에 또 다른 소비자용 상품 후보를 검토할 때도 목록을 새로 만들 필요 없이, 같은 구분 값을 붙이기만 하면 지금 만든 채점 절차를 그대로 탈 수 있다.
상품 22건을 기계로 전수 채점했다
설계를 반영한 뒤 상품 후보 22건 전부를 새 채점표로 돌렸다. 통과 기준은 18점 만점에 12점 이상이었다. 채점은 앞서 확인한 세 실측값(도소매업 순소득률 6.38%, 광고 최저 입찰가 780~13,280원, 소매 단가 관측 3/22건)을 그대로 입력값으로 썼다 — 사람이 상품을 다시 들여다보고 점수를 매기는 것이 아니라, 이미 확보한 숫자를 채점표 규칙에 그대로 대입하는 방식이다.
채점은 사람이 아니라 스크립트가 수행했다. 상품 후보 22건 각각에 대해 소매 단가가 있으면 그 단가에서 원가를 뺀 건당이익을 계산하고, 그 이익을 해당 카테고리의 광고 최저 입찰가와 나눠 배율을 냈다. 이 배율과 나머지 항목 점수를 합쳐 18점 만점의 채점표에 대입하는 방식이다. 사람이 개입하는 지점은 채점표 규칙을 설계하는 단계뿐이고, 실제로 22건에 점수를 매기는 과정에는 사람의 판단이 들어가지 않는다.
돌리기 전 예상은 '몇 건은 통과하고 몇 건은 떨어질 것'이었다. 이미 수집 시스템을 통과해 관심 신호를 만들어 낸 상품들이었기 때문에, 적어도 절반 가까이는 기각선을 넘길 것으로 봤다. 실제 결과는 이 예상과 완전히 달랐다 — 다음 절에서 그 결과를 그대로 옮긴다.
결과: 22건 중 통과 0건, 최고점 9/18
전수 채점 결과 22건 중 기각선(12/18점)을 넘은 상품은 0건이었다. 최고점은 9점(텀블러, 계절가전)에 그쳤다. 이걸 채점표가 잘못됐다는 신호로 보지 않은 이유는, 낮은 점수의 원인이 전부 앞서 확인한 실측값으로 설명되기 때문이다.

텀블러는 건당이익 2,752원, 광고 최저 입찰가 2,430원으로 비율이 1.13배다. 뷰티 디바이스는 건당이익 2,074원, 광고 최저 입찰가 2,130원으로 비율이 0.97배 — 클릭 1회 값을 건당이익이 넘지 못한다. 방문자 100명 중 1명이 실제로 산다고 가정하면(전환율 1%), 광고 클릭 100번 중 1번만 판매로 이어지므로 고객 1명을 데려오는 데 드는 비용은 건당이익의 100배가 필요 하다는 계산이 나온다.
단가가 가장 높은 계절가전만 배율 5.28배로 셋 중 유일하게 여유가 있었지만, 그마저도 재구매가 0으로 관측돼 총점 9점에서 더 올라가지 못했다. 광고비를 감당할 여지가 가장 큰 상품조차 필요한 100배에서 20배 가까이 모자랐다는 뜻이고, 나머지 두 상품은 클릭 1회 비용조차 못 넘는 수준이었다.
여기서 쓰인 CAC(Customer Acquisition Cost)란 고객 1명을 실제로 데려오는 데 드는 총비용을 뜻한다. 클릭 1회 값(광고 최저 입찰가)과 전환율(클릭한 사람 중 실제로 사는 비율)을 알면 CAC를 역산할 수 있다 — 전환율이 1%라면 구매 1건을 만들기 위해 클릭이 평균 100번 필요하므로, CAC는 클릭 1회 비용의 100배가 된다. 건당이익이 이 CAC보다 작으면 광고를 태울수록 오히려 손해가 커지는 구조다.
검증은 채점 결과 외에도 여러 층에서 돌렸다. 절차 문서가 요구하는 기계 판정 7개 중 7개, 점검 항목 11개 중 11개를 통과했고, 일부러 조건을 깨뜨려 보는 적대적 테스트 5개(마진 미만 강제·마진 충족 유지·기업용 무영향·신청 정보 누락 거부·단독 조건 거부)도 5개 모두 의도한 대로 거부됐다. 기업용 목록 129건을 다시 채점해도 기존 통과 87건은 그대로였고 재채점·재발번은 0건이었다 — 새 채점표를 붙인 작업이 기존에 이미 통과해 있던 기업용 상품들에는 아무 영향을 주지 않았다는 뜻이다.
- 소비자용 상품 후보는 자동 콜드메일 발송 경로에 들어가지 못하게 입구 한 곳을 막았다 — 이 후보는 받는 사람이 회사가 아니라 상품 카테고리라 애초에 메일을 보낼 대상이 없다. 이 경로로 새는 사례는 0건이었다.
- 코드를 점검하다가 파일 경로를 코드 안에 직접 박아 둔 부분 1건을 자체 발견해, 공용 설정에서 읽어오는 방식으로 고쳤다. 수정 전 1건, 수정 후 0건.
적대적 테스트란 채점표가 통과시켜야 할 경우와 거부해야 할 경우를 일부러 만들어 넣어 보는 방식이다. 예를 들어 마진 조건을 못 채우는 상품을 넣었을 때 실제로 거부되는지, 반대로 조건을 채우는 상품을 넣었을 때 정말 통과되는지를 하나씩 확인한다. 다섯 가지 경우 전부 의도한 대로 움직였다는 것은, 채점표가 우연히 0건이라는 결과를 낸 것이 아니라 설계한 규칙대로 정확히 작동했다는 뜻이다.
단가를 못 잰 19건은 '안 남는다'가 아니라 '아직 모른다'
남은 미지수는 단가 하나다. 22건 중 19건이 소매 단가를 실측하지 못해 '단가' 항목이 0점으로 처리됐는데, 이건 '이 상품은 안 남는다'가 아니라 '아직 재지 못했다'는 뜻이다. 추정치를 임의로 채워 넣지 않고 0점으로 두되, 미실측이라는 사실을 채점 기록에 따로 남겼다. 모른다는 사실과 0이라는 점수를 구분해 두지 않으면, 나중에 이 목록을 다시 볼 사람이 '재보니 안 남는 상품 19개'로 잘못 읽는다.
단가가 안 들어온 이유를 거슬러 올라가 보니 더 큰 문제가 나왔다. 소비자 관심 상품 신호를 모으는 수집 시스템 자체가 2026년 8월 12일 무렵 멈춰 있었다. 신호를 담는 저장함이 0건, 상품 코드 수집도 0건이었고, 그나마 남아 있던 패턴 데이터는 1,090건(가장 최근 기록이 2026년 8월 13일)에서 멈춰 있었다.

날짜별 건수를 세어 보면 2026년 8월 1일부터 10일까지는 하루 평균 약 60건이 들어오다가, 8월 11일 26건, 8월 12일 13건, 8월 13일 1건으로 사흘 만에 꺾였다. 이 시스템은 이번 작업의 범위 밖이라 수리는 별도 과제로 미뤘지만, 그대로 두면 상품 후보가 '새로 들어온 신호'처럼 보여도 실제로는 전부 옛날 신호인 조용한 실패가 이어진다. 겉보기 목록은 매번 채워져 있으니 아무도 이상하다고 느끼지 못하는 상태다. 그래서 이 상태 자체를 매일 아침 자동으로 알려주는 감시 장치를 새로 걸어 두었고, 실제로 알림이 뜨는 것까지 확인했다.
'조용한 실패'라는 표현을 쓴 이유는, 이 상태가 오류 메시지를 내거나 화면이 깨지는 방식으로 드러나지 않기 때문이다. 목록에는 여전히 숫자가 채워져 있고 시스템은 '작동 중'으로 보인다. 겉으로 멀쩡해 보이는 상태에서 속만 비어 가는 이런 실패는, 누군가 일부러 날짜별 추이를 세어 보기 전까지는 아무도 알아채지 못한다.
정리하면 이번 채점의 결론은 '지금 갖고 있는 22건은 팔지 않는다'다. 다만 이 결론에는 조건이 하나 붙는다 — 19건의 실제 판매 단가를 재기 전까지는 그중 일부가 계절가전처럼 높은 단가로 뒤집힐 가능성이 남아 있다는 뜻이다. 판정을 내리는 절차 자체는 완성됐고, 남은 건 입력값을 마저 채우는 일이다.
이번에 확인한 원칙 — 마진이 광고 최저 입찰가를 이기는지부터 계산한다 — 은 위탁판매뿐 아니라 검색 기반으로 고객을 모으는 모든 판매 구조에 그대로 적용된다. 비슷한 문제를 이미 콘텐츠 쪽에서 겪은 기록은 SEO 콘텐츠 노출이 안 붙은 원인에도 남아 있다. 상품이나 서비스를 새로 팔기 전에 이런 채점 절차가 필요하면 상담을 통해 문의해도 된다.
비슷한 처지의 소상공인이나 위탁판매 창업을 검토하는 사람에게 남기고 싶은 실무 순서는 간단하다 — 상품이 마음에 드는지보다 먼저, 그 상품의 건당이익이 해당 카테고리 광고 최저 입찰가의 몇 배인지부터 계산해 본다. 100배에 한참 못 미친다면 광고를 태워서는 답이 안 나오는 구조라는 뜻이고, 재구매나 다른 유입 경로로 그 격차를 메울 수 있는지를 그다음에 따져야 한다.