sgkstudio.
엔지니어링

추천 알고리즘 편향 — 점수 대신 쿼터를 쓴 이유

추천 알고리즘 편향은 점수식 자체가 아니라, 점수만으로 순서를 정하는 구조 안에 있었습니다. 회사 기술 블로그의 글감 선택 점수는 '글이 될 재료가 얼마나 많은가'만 쟀고, 실제로 파는 서비스와 관련된 소재 후보 3건은 전체 순위에서 각각 561위, 664위, 936위에 머물러 있었습니다. 하루 3~9편을 발행하는 페이스로도 그 자리까지 내려가려면 약 두 달이 걸리는 순위였습니다. 점수에 가산점을 더하는 방법은 시뮬레이션에서부터 실패했고, 점수식은 그대로 두고 별도의 확보 규칙을 추가해서야 문제가 풀렸습니다.

2026-09-29판매 서비스 소재 3건 순위 실측 — 561위·664위·936위, 하루 3~9편 발행 기준 도달까지 약 두 달가산점 시뮬레이션 — 최대 +35점을 줘도 129위, 기본 점수(본문 946자) 격차를 못 메움조치 후 재측정 — 선택 소재 95점→72점 반전, 카테고리 내 순위 24위(2~3주 내 도달 예상), 테스트 6건 추가 24건 통과분류 어휘 정밀도 조정 — 판매 서비스 분류 판정률 13%→4.8%(88/1842)검색 노출 3주 추이 — 1,826회→1,345회→1,040회(43% 감소), 구매의도 검색어 커버 0/15·0/15·0/17

우선순위 점수만으로 콘텐츠를 고르면 왜 밀리는 소재가 생길까?

발행 파이프라인은 매일 여러 소재 후보를 모으고 점수를 매겨 가장 높은 것부터 순서대로 뽑습니다. 방식 자체는 흔하고 합리적으로 보입니다. 문제는 점수가 재는 값과 실제로 필요한 값이 다를 때 생깁니다.

이런 형태의 편향은 저희만 겪는 문제가 아닙니다. 점수·순위·추천처럼 항목을 자동으로 줄 세우는 시스템은 어디서나 '측정하기 쉬운 값'을 '중요한 값'으로 착각하기 쉽습니다. 측정하기 쉬운 값(재료의 양)과 실제로 필요한 값(판매로 이어지는 실체)이 다르면, 아무리 정직하게 점수를 매겨도 후자는 구조적으로 밀립니다.

한 잠재고객이 남긴 문의가 정확히 그 지점을 건드렸습니다. 문의는 회계팀이 손으로 하던 거래명세서 대조 작업(거래처가 보낸 명세서와 회사 장부를 맞춰보는 회계 작업, 이 글에서는 '대사'라고 부릅니다)을 자동화해 달라는 내용이었고, 저희는 이미 그 업무를 다루는 소재 후보 3건을 갖고 있었습니다. 그런데 전체 순위표를 열어 보니 세 후보는 각각 561위, 664위, 936위였습니다.

하루 3~9편을 발행하는 페이스에서 561위까지 내려가려면 대략 두 달이 걸립니다. 발행 속도는 얼마 전 따로 정한 값입니다.

기존에 홈페이지에 쌓고 있던 SEO 콘텐츠들이 영 결과가 좋지 않아서 이 기술 블로그가 사실상 콘텐츠 마케팅을 다시 시작하는 거라 속도가 좀 중요한데

속도가 중요하다고 정해 둔 상황에서 정작 팔리는 일을 다룬 글감이 두 달 뒤에나 나온다면, 점수표가 뭔가를 놓치고 있다는 뜻이었습니다.

이 사례를 기록으로 남기는 이유도 여기에 있습니다. 같은 파이프라인을 오래 쓰다 보면 점수식이 처음 설계된 목적과 실제로 재는 값이 조금씩 어긋나는 순간이 옵니다. 그 어긋남은 코드만 읽어서는 잘 보이지 않고, 실제로 뽑힌 결과를 세어 봐야 드러납니다.

이 발행 속도는 원래부터 이렇게 빠르지 않았습니다. 이전에는 실행이 첫 통과에서 멈추는 구조라 하루 최대 1편이 한계였고, 그 구조를 상한까지 이어가도록 고쳐 하루 최대 5편, 시도는 최대 8회까지 늘렸습니다. 다섯 편이 다 통과하는 데는 보통 3~4시간이 걸리고, 여덟 번을 다 써도 최대 4시간을 넘기지 않아 아침에 실행해도 점심 전에는 끝납니다. 그렇다고 열 편까지 올리지는 않았는데, 비슷한 규모 스튜디오의 발행 속도가 회사당 주 1~3편인 것과 비교하면 하루 열 편은 격차가 너무 커서 오히려 신뢰를 깎는 쪽으로 읽힐 수 있었기 때문입니다.

발행 31편 중 판매 서비스 글은 0편 — 처음 잰 숫자

순위를 확인하기 전에 실제로 무엇이 나가고 있는지부터 셌습니다. 같은 시점에 이미 발행된 31편의 주제를 전수로 훑었습니다.

31편 전부 회사 안의 개발 도구 이야기였습니다. 검색 API 연동, 작업 감지 로직, 데이터 수집 방식처럼 내부 인프라를 다룬 글이 자리를 채우고 있었고, 실제로 파는 서비스를 다룬 글은 한 편도 없었습니다.

원인은 점수 계산 방식에 있었습니다. 점수는 본문에 등장하는 숫자의 개수와, 본문 250자당 1점씩 쌓이는 길이 점수 두 가지만 쟀습니다. 한마디로 '글이 될 재료가 얼마나 많은가'만 재는 식입니다.

그런데 대사 자동화 소재의 본문은 946자였습니다. 개발 도구 작업은 커밋 메시지와 로그가 길게 남아 재료가 저절로 쌓이지만, 새로 맡은 업무 이야기는 아직 데이터가 얇습니다. 재료의 양과 우리가 파는 일인가는 원래 다른 질문인데, 점수식은 그 둘을 하나로 묶어서 재고 있었습니다.

이 점수식이 원래 나쁜 설계였던 건 아닙니다. 개발 작업처럼 로그와 커밋 메시지가 저절로 쌓이는 소재에서는 재료의 양이 글의 완성도와 비슷하게 움직였습니다. 문제는 판매 서비스 관련 소재처럼 재료가 아직 얇은 카테고리가 생기는 순간, 같은 잣대가 그 카테고리를 구조적으로 밀어낸다는 점이었습니다.

소재가 부족해서 생긴 문제가 아니었다는 것도 짚어 둘 필요가 있습니다. 같은 시기 별도로 진행한 조사에서는 판매 서비스와 관련된 인용 가능 회사가 164개사까지 확보돼 있었고, 이건 전체 공고 33,399건 가운데 본문까지 읽은 3,593건(10.8%)만 반영한 숫자였습니다. 나머지를 마저 읽으면 신호는 더 늘어날 상황이었습니다. 문제는 신호가 없어서가 아니라, 확보된 신호를 기술 블로그 한 편으로 옮기는 길목에서 점수식이 막고 있었다는 데 있었습니다.

이 격차를 다르게 표현하면 이렇습니다. 개발 도구 이야기는 코드를 고칠 때마다 로그와 커밋 메시지, 에러 문구가 저절로 쌓이는 반면, 새로 맡은 업무 이야기는 사람이 따로 정리해야만 재료가 생깁니다. 점수식은 이 차이를 몰랐고, '아직 재료가 적다'와 '중요하지 않다'를 구분하지 못했습니다.

판매 서비스 소재 3건의 전체 순위
후보전체 순위
후보 1561위
후보 2664위
후보 3936위

세 후보 모두 같은 업무(거래명세서 대사)를 다룬 소재였고, 셋 다 상위권과는 거리가 멀었습니다. 그날 발행되는 한 편은 언제나 1위 후보였습니다.

가설: 점수식에 가산점을 더하면 순위가 오를 것이다

첫 대응은 점수식을 고치는 쪽이었습니다. 판매 서비스와 얼마나 가까운 소재인지를 가산점으로 더하면, 재료가 적어도 순위가 오를 것이라는 가설이었습니다.

가산점을 고른 이유는 간단했습니다. 점수식을 통째로 바꾸는 대신 항목 하나만 추가하면 되니 수정 범위가 가장 작아 보였습니다. 다만 그 간단함이 맞는 방향인지는 실제로 적용하기 전에 먼저 확인해야 했습니다.

실제로 적용하기 전에 먼저 시뮬레이션으로 가산점 값을 여러 단계로 올려 보며 순위가 얼마나 움직이는지 확인했습니다. 가장 크게는 +35점까지 얹어 봤습니다.

가산점 +35를 줘도 순위가 129위에 머문 이유는 무엇이었을까?

결과는 기대에 못 미쳤습니다. 최대치인 +35점을 줘도 대사 소재는 129위에 그쳤습니다. 561위에서 129위로 올라오긴 했지만, 그날 발행되는 한 편 안에 들려면 한 자릿수 순위까지 와야 합니다.

이유는 기본 점수 격차가 너무 컸기 때문입니다. 946자짜리 소재의 기본 점수는 개발 도구 소재들과 애초에 큰 차이가 났고, 그 격차를 가산점으로 메우려면 가산점 자체가 점수 전체를 지배할 만큼 커져야 했습니다. 그렇게 되면 판매 서비스로 분류된 소재들 사이의 순서까지 가산점이 정하게 되고, 점수식이 원래 재려던 값(재료의 질)은 의미를 잃습니다. 가산점 방식은 여기서 기각했습니다.

가산점을 계속 올리는 방향도 검토했습니다. 그런데 격차를 완전히 메울 만큼 큰 값을 주면, 그 값이 사실상 점수식을 대체하는 것과 같아집니다. 점수식이 있는 이유 자체가 사라지는 셈이라, 이 방향은 값을 얼마나 키우든 근본적으로 같은 문제로 되돌아왔습니다.

진짜 원인 — 검색은 발견이 아니라 검증이었다

점수식을 만지는 방향이 막히자 질문을 바꿨습니다. 애초에 왜 판매 서비스 이야기가 필요한가부터 다시 봤습니다.

같은 시기에 따로 진행한 검색 노출 조사에서 답이 나왔습니다. 이 회사 홈페이지로 들어오는 검색은 몰라서 찾아보는 발견형 검색이 아니라, 이미 접촉한 사람이 회사 이름을 확인하려고 치는 검증형 검색이었습니다. 검증형 검색에서 필요한 건 검색 순위가 아니라, 검색해서 들어왔을 때 보이는 실체입니다. 개발 도구 이야기는 그 자리에서 '이 일을 할 수 있다'는 증거로 읽히지 않습니다.

숫자로도 같은 그림이 나왔습니다. 검색 노출은 2026년 8월 24일 1,826회에서 2026년 8월 31일 1,345회, 2026년 9월 6일 1,040회로 3주 동안 43% 줄었습니다. 같은 3주 동안 구매 의도가 있는 검색어의 노출 커버는 0/15, 0/15, 0/17로 그대로였고, 남은 노출 1,040건 중 516건은 이미 접은 서비스와 관련된 검색어였습니다. 판매 서비스 페이지 자체 클릭은 28일간 2회에 그쳤습니다.

정확히 말하면 검증형 검색에서 필요한 건 '검색했을 때 상위에 뜨는가'가 아니라 '검색해서 들어왔을 때 무엇이 보이는가'입니다. 이미 회사 이름을 알고 검색한 사람에게는 순위 1위든 3위든 큰 차이가 없고, 그 자리에서 실제로 그 업무를 할 수 있다는 근거가 있는지가 갈립니다.

속도가 사는 것과 안 사는 것도 구분해야 했습니다. 색인 20편이라는 목표는 하루 5편이면 나흘 만에 채울 수 있지만, 자체 도메인에 이미 쌓여 있던 154편이 5주 동안 순위를 한 칸도 움직이지 못했다는 관측은 이 축에도 그대로 적용됩니다. 발행 편수는 블로그가 살아 있고 깊다는 신뢰는 사지만, 검색 권위 자체를 사지는 않습니다. 그래서 이번 조치의 목표도 순위를 올리는 데 두지 않고, 검증형 검색이 들어왔을 때 보여줄 실체를 늘리는 쪽으로 다시 잡았습니다.

막대그래프. 검색 노출이 2026년 8월 24일 1,826회, 8월 31일 1,345회, 9월 6일 1,040회로 3주간 43% 감소
노출은 줄었지만 원인은 다른 곳에 있었다

즉 검색 순위를 더 올리는 방향은 답이 아니었습니다. 진짜 부족한 건 검색해서 들어왔을 때 보여줄 실체였고, 그 실체를 만드는 첫걸음이 판매 서비스와 관련된 소재를 꾸준히 내보내는 일이었습니다.

조치 — 점수 대신 쿼터를 넣었다

점수식은 그대로 두고 별도의 확보 규칙을 추가하기로 했습니다. 최근 4편 안에 판매 서비스와 관련된 소재가 한 편도 없으면, 전체 순위와 무관하게 그 카테고리에서 점수가 가장 높은 후보를 먼저 뽑습니다.

  • 기각안 1 — 가산점으로 점수식 자체를 조정 (앞 섹션 시뮬레이션에서 기각)
  • 기각안 2 — 대사 소재 한 편을 손으로 끼워 넣기 (다음에 같은 문제가 또 생기고, 선택 규칙 자체를 우회하는 방식)
  • 기각안 3 — 발행량을 늘려 561위까지 순서대로 소진 (1인 스튜디오 블로그에 과도한 발행량이라는 판단)

세 방법 모두 문제를 소재 하나 단위에서 풀거나, 발행량이라는 다른 축을 희생해서 풀려고 했습니다. 필요한 건 점수 구조를 유지한 채로 특정 카테고리의 최소 출현을 보장하는 장치였습니다.

확보 규칙이 보는 범위 자체도 하나의 설계 값입니다. 최근 몇 편을 기준으로 볼지에 따라 규칙이 얼마나 자주 발동하는지가 달라집니다. 범위를 너무 좁게 잡으면 발동이 뜸해지고, 너무 넓게 잡으면 점수 구조가 거의 무의미해집니다. 최근 4편이라는 값은 하루 3~9편을 발행하는 속도에서 며칠 안에 한 번은 반드시 확인이 도는 정도로 잡은 값입니다.

첫 구현에는 버그가 있었습니다. 판매 서비스 소재인지 가리는 단어 매칭을 '블로그'·'광고' 단독 매칭으로 짰더니, 회사 자체의 기술 블로그 운영 이야기까지 판매 서비스 소재로 분류됐습니다. 그 결과 최근 발행분 자리가 가짜 판매 서비스 소재로 채워지고, 정작 쿼터가 한 번도 발동하지 않는 상태가 됐습니다. 단어 매칭을 정밀도 우선으로 다시 잡고서야 이 문제가 풀렸습니다.

정밀도를 올리는 쪽으로 방향을 잡은 이유도 있습니다. 매칭을 넓게 잡으면 확보 규칙이 자주 발동해 보이지만, 실제로는 엉뚱한 소재가 자리를 채워 진짜 필요한 소재를 오히려 늦춥니다. 느리더라도 정확하게 가려내는 쪽이, 빠르지만 틀리게 가려내는 쪽보다 이 규칙의 목적에 맞았습니다.

이 버그를 알아챈 계기도 단순했습니다. 확보 규칙을 켠 뒤에도 선택된 소재 목록을 열어 보면 판매 서비스 소재가 아니라 블로그 운영 자체를 다룬 글이 반복해서 올라와 있었습니다. 카테고리 자리는 채워졌는데 정작 원하던 내용은 하나도 없는 상태였습니다.

확보 규칙을 넣었다고 다른 카테고리가 희생되지는 않습니다. 최근 4편에 판매 서비스 소재가 이미 있으면 규칙은 발동하지 않고 점수 순서 그대로 뽑습니다. 규칙이 작동하는 경우는 그 카테고리가 정말 비어 있을 때로 좁혀 둔 셈입니다.

이 파이프라인에서 소재가 조용히 막히는 문제를 겪은 건 이번이 처음이 아닙니다. 앞서 새 재료 창구 하나가 후보를 3건 쌓아 두고도 발행은 0편이던 사례를 콘텐츠 자동화 파이프라인 0편 — 소재는 3건 쌓여 있었다에 따로 정리했습니다. 그때는 경로를 못 읽는 문제와 길이 점수 문제였고, 이번에는 순위 자체가 구조적으로 밀리는 문제였습니다. 같은 파이프라인 안에서도 무음 실패의 형태는 매번 달랐습니다.

재측정 — 선택이 실제로 뒤집혔다

규칙을 반영한 뒤 실제 무인 실행에서 선택이 바뀌었는지 확인했습니다. 조치 전이라면 95점짜리 다른 프로젝트 소재가 뽑혔을 자리에서, 조치 후에는 72점짜리 판매 서비스 소재가 선택됐습니다. 순위가 낮아도 카테고리 확보 규칙이 우선한 결과입니다.

대사 소재의 순위도 함께 올랐습니다. 전체 순위 561~936위였던 소재가 카테고리 안에서는 24위로 올라왔고, 이 페이스면 2~3주 안에 실제 발행까지 도달할 것으로 봤습니다.

막대그래프. 가산점 없이 561위, 가산점 +35 시뮬레이션에서 129위, 쿼터 적용 후 실제 순위 24위
순위를 바꾼 건 가산점이 아니라 확보 규칙이었다

숫자를 다시 정리하면, 조치 전에는 다른 프로젝트 소재가 항상 이겼고, 조치 후에는 확보 규칙이 켜진 상태에서 점수가 더 낮은 판매 서비스 소재가 선택됐습니다. 점수가 낮은 항목이 선택됐다는 사실 자체가, 이 장치가 순위표를 무시하지 않으면서도 카테고리를 확보한다는 증거입니다.

검증은 테스트로도 했습니다. 새 규칙을 위해 테스트 6건을 추가해 전체 24건이 통과했고, 쿼터 로직만 빼고 같은 테스트를 다시 돌리면 그중 1건만 실패합니다. 이 규칙이 실제로 판정을 가르고 있다는 뜻입니다.

이번 조치에서 점수식 코드 자체는 한 줄도 바꾸지 않았습니다. 바뀐 건 그 점수식으로 정렬한 뒤, 뽑기 직전에 확보 규칙을 한 번 더 통과시키는 순서 하나였습니다. 그래서 기존에 통과하던 다른 테스트들도 전부 그대로 통과했고, 쿼터 로직만 빼면 실패하는 테스트가 정확히 1건이라는 사실이 이 장치가 딱 그 자리만 건드렸음을 보여줍니다.

이 변화가 보여주는 지점은 단순합니다. 같은 점수표 안에서도 특정 카테고리를 반드시 보이게 하는 규칙은 점수와 별도로 둘 수 있었고, 그 결정에는 코드 한 줄이 아니라 규칙 하나면 충분했습니다.

단어 매칭을 정밀도 우선으로 조정한 결과도 함께 바뀌었습니다. 판매 서비스로 분류되는 소재의 비율은 13%에서 4.8%(88/1842)로 줄었습니다. 분류되는 소재 수는 줄었지만, 그만큼 회사 자체 블로그 운영기 같은 가짜 매칭이 빠지고 실제로 판매 서비스와 관련된 소재만 남았다는 뜻입니다.

표. 무인 실행 선택 소재 95점에서 72점, 순위 561~936위에서 24위, 분류 판정률 13%에서 4.8%(88/1842), 테스트 6건 추가 24건 통과
조치 전후 — 선택이 실제로 뒤집혔다

표로 보면 바뀐 항목은 넷입니다. 선택된 소재, 그 소재의 순위, 분류 판정률, 테스트 통과 수입니다. 넷 중 어느 하나도 점수식을 직접 건드려 만든 변화가 아니라, 확보 규칙과 분류 어휘 두 가지를 고친 결과였습니다.

쿼터를 넣어도 안 풀리는 건 무엇일까?

규칙을 반영한 다음 날인 2026년 9월 8일 새벽 2시 20분 무인 실행 로그에서 판매 서비스 소재가 실제로 선택되는지가 다음 판정 기준이었습니다. 이 글을 쓰는 시점에는 아직 그 결과를 반영하지 못했고, 선택되지 않으면 쿼터가 확인하는 발행 범위(최근 몇 편을 볼지 정하는 값)를 다시 조정할 예정입니다.

판정이 나오기 전까지는 이 조치가 통했다고 말할 수 없습니다. 순위와 분류 판정률처럼 파이프라인 안에서 측정한 값은 실제로 움직였지만, 그 소재가 무인 실행에서 최종적으로 선택돼 발행까지 가는 것은 다음 실행 로그를 열어야 확인되는 별개의 단계입니다. 확인 없이 '고쳤다'고 정리하면, 앞서 재료 부족을 의심했던 것과 같은 실수를 반복하는 셈입니다. 순위가 오른 것과 발행되는 것은 서로 다른 사실입니다.

규칙 하나로 안 풀리는 문제도 남아 있습니다. 판매 서비스 페이지 클릭이 28일간 2회에 그친 데서 보이듯, 검색해서 들어왔을 때 보여줄 사례 자체가 아직 얇습니다. 소재가 더 자주 나가게 만드는 것과, 그 소재가 실제로 신뢰를 주는 실체가 되는 것은 다른 문제이고, 후자는 이번 조치 하나로 끝나지 않습니다. 카테고리를 채우는 규칙과 그 카테고리를 신뢰로 바꾸는 작업은 나란히 가야 합니다.

쿼터 확인 범위를 조정하는 방법 말고 남은 선택지도 있습니다. 확인 범위를 넓히는 대신, 판매 서비스로 분류하는 기준 자체를 조금 더 관대하게 돌려 대상 후보 풀을 넓히는 방향도 함께 검토할 예정입니다. 다만 그 방향은 정밀도를 낮추는 것과 같은 트레이드오프를 다시 불러오므로, 무엇을 조정할지는 다음 실행 로그를 본 뒤에 정합니다.

  • 2026년 9월 8일 새벽 2시 20분 무인 실행에서 판매 서비스 소재가 실제로 선택되는가
  • 선택되지 않으면 확인 범위(최근 몇 편)를 넓히는가, 분류 기준을 넓히는가
  • 판매 서비스 페이지 클릭이 28일간 2회에서 실제로 늘어나는가

이 사례가 남긴 규칙은 명확합니다. 점수가 재는 값과 실제로 필요한 값이 다르면, 점수식을 계속 손보기보다 필요한 만큼만 확보하는 규칙을 따로 두는 편이 더 작은 변경으로 더 정확하게 문제를 풉니다.

비슷하게 자동으로 콘텐츠나 후보를 고르는 파이프라인에서 특정 카테고리가 계속 점수 뒤로 밀리는 문제를 겪고 계시다면 상담으로 상황을 알려 주세요.

같은 방식으로 우리 업무를 재보고 싶다면

이 글에 쓴 계측·판정·자동화는 저희가 실제로 매일 돌리는 방식 그대로입니다. 어떤 작업을 자동화할 수 있고 어디를 사람이 잡아야 하는지, 현재 업무 흐름을 놓고 같이 짚어 드립니다.

새 글이 올라오면 알려 드립니다

이런 계측 기록을 새로 쓰면 메일로 한 통 보냅니다. 광고나 판촉은 보내지 않습니다.