관련 글 목록은 왜 화면 한 장을 차지했나?
관련 글 목록이 화면 한 장을 차지한 이유는 글 하나가 사이트의 다른 글 전부를 가리키고 있었기 때문입니다. 시작은 운영 쪽에서 나온 짧은 지적이었습니다.
리포트 하단 관련 인사이트 리스트가 너무 조잡하다.
열어 보니 낱개 리포트 페이지가 전체 글 목록을 불러오는 함수(`allReports()`)에서 자기 자신만 빼고 나머지 101편을 전부 나열하고 있었습니다. 제목은 22자에서 잘려 말줄임표(…)가 붙어 있었습니다. 어느 글이 어떤 내용인지 제목만으로는 알 수 없는 링크 101개가 글 아래에 줄지어 있던 셈입니다.
이 구조는 처음부터 문제를 일으키지는 않았습니다. 리포트가 52편일 때는 목록이 길어도 눈에 덜 띄었습니다. 글이 102편으로 늘자 목록이 본문 아래 화면 한 장을 통째로 먹었습니다. 글을 꾸준히 쌓는 사이트라면 같은 코드가 글 수에 비례해 점점 나빠집니다. 아무도 코드를 바꾸지 않았는데 사이트가 나빠지는 종류의 문제입니다.
비용은 두 갈래였습니다. 하나는 독자 쪽입니다. 글을 다 읽고 다음 글을 찾는 사람에게 잘린 제목 101개는 고를 수 있는 목록이 아니라 벽입니다. 다른 하나는 검색엔진 쪽이고, 이 글이 다루는 쪽이기도 합니다. 내부 링크 SEO에서 링크는 "이 페이지가 중요하다"는 신호인데, 한 페이지에서 100개 넘는 내부 링크가 나가면 그 신호가 모든 글에 똑같이 흩어집니다.
처음 잰 숫자: 글 하나가 101편을 가리켰다
측정은 빌드 결과물로 했습니다. 사이트는 글마다 HTML 페이지를 빌드 때 미리 만들어 두는 방식이라, 만들어진 HTML을 열면 독자와 검색엔진이 보는 링크를 그대로 셀 수 있습니다. 낱개 리포트 페이지가 다른 리포트 페이지로 거는 링크 수를 셌습니다.

숫자는 단순했습니다. 리포트가 102편이니 글 하나가 거는 리포트 링크는 101개, 즉 자기를 뺀 전부였습니다. 102편 전부가 같은 구조라서, 사이트 안의 모든 글이 모든 글을 가리키고 있었습니다. 이 상태를 그림으로 그리면 중심이 없는 완전 그래프가 나옵니다.
코드 쪽 원인도 이 단계에서 확인했습니다. 낱개 글 페이지는 전체 글을 최신순으로 돌려주는 함수를 부르고, 결과에서 자기 주소 하나만 걸러 그대로 화면에 뿌렸습니다. 거르는 조건이 "나 자신이 아닌가" 하나뿐이라, 글이 한 편 늘 때마다 모든 페이지의 링크도 하나씩 늘었습니다. 102편째 글을 올리는 순간 이미 올라가 있던 101편의 페이지가 전부 링크 하나씩을 더 갖게 됩니다.
이 숫자가 왜 문제인지는 링크가 하는 일을 떠올리면 보입니다. 검색엔진은 사이트 안에서 링크를 많이 받는 페이지, 그리고 중요한 페이지에서 링크를 받는 페이지를 더 중요하게 읽습니다. 그런데 모든 글이 모든 글에 링크를 똑같이 하나씩 주면, 받는 링크 수가 모든 글에서 같아집니다. 사이트 구조가 평평해지고, 어느 글이 중심인지 알려 줄 신호가 사라집니다.
측정 기준도 여기서 정했습니다. 고친 뒤에도 같은 빌드 HTML 102편을 전수로 열어, 낱개 글이 다른 리포트로 거는 링크의 최댓값을 봅니다. 한두 페이지만 열어 보고 "줄었다"고 말하지 않기 위해서입니다.
세운 가설: 보기 문제라면 가리거나 줄이면 된다
처음 떠오른 해법은 전부 "목록이 길어서 보기 싫다"는 진단에서 나왔습니다. 그 진단이 맞다면 목록을 가리거나 줄이기만 하면 됩니다. 실제로 검토한 해법은 세 가지였고, 여기에 렌더 단계에서 하나가 더 붙었습니다.
- 가설 1: 목록을 접어 두면 된다. 기본으로 몇 개만 보여 주고 "더보기"를 누르면 101개가 펼쳐지게 한다
- 가설 2: 무작위로 N편만 보여 주면 된다. 매번 다른 글이 보이니 모든 글에 고르게 기회가 돌아간다
- 가설 3: 태그가 겹치는 글만 보여 주면 된다. 각 글에 붙은 태그로 비슷한 글을 고른다
- 가설 4: 카드에 붙는 주제 뱃지는 기존 분류(이 글이 어떤 판매 서비스와 연결되는지)를 그대로 쓰면 된다
네 가설은 모두 그럴듯했습니다. 접기는 화면 문제를 바로 없애고, 무작위는 공평해 보이고, 태그는 이미 있는 데이터를 씁니다. 뱃지 가설은 고민할 거리조차 아닌 것처럼 보였습니다. 분류가 이미 있으니 그걸 찍으면 그만이라고 생각했습니다.
이런 판단은 SEO 콘텐츠를 대량으로 만들 때 자주 나옵니다. 페이지 수가 늘어난 뒤에 생긴 문제를 화면 문제로 보고 화면만 손보는 식입니다. 비슷한 상황에서 페이지 수 자체가 문제가 된 사례는 프로그래매틱 SEO로 만든 키워드 페이지가 얇은 콘텐츠로 읽힌 사례에 따로 정리해 두었습니다.
가설은 어디서 틀렸나?
네 가설 중 셋은 설계를 검토하는 단계에서, 하나는 첫 렌더 화면을 실측하는 단계에서 무너졌습니다.

접기는 가장 먼저 탈락했습니다. 접어 둔 목록도 HTML 안에는 101개 링크가 그대로 남습니다. 검색엔진은 접힌 링크도 링크로 읽기 때문에 링크 희석은 하나도 줄지 않습니다. 독자 눈에서만 사라질 뿐이라, 문제를 고치는 대신 가리는 해법이었습니다.
무작위 N편은 화면도 링크 수도 줄이지만 다른 문제를 만듭니다. 빌드할 때마다 링크 대상이 바뀌니, 미리 만들어 둔 페이지가 배포 때마다 다른 내용이 됩니다. 검색엔진 입장에서는 같은 주소의 링크 구성이 계속 흔들리는 페이지입니다. 결과가 매번 달라지니 "이 글 아래에는 이 6편이 나와야 한다"는 회귀 테스트도 쓸 수 없습니다.
태그만으로 매칭하는 안은 데이터 품질에서 막혔습니다. 글마다 붙은 태그는 품질을 검증한 적이 없었습니다. 검증하지 않은 데이터를 추천의 유일한 기준으로 쓰면 목록이 그럴듯해 보여도 왜 그 글이 나왔는지 설명할 수 없습니다. 반면 판매 서비스 분류는 서비스 목록 파일(`data/ads/offers.json`)과 물려 있어 이미 믿을 수 있는 분류였습니다.
뱃지 가설은 화면을 띄우고 나서야 틀린 걸 알았습니다. 1차 렌더에서 부가세 글 카드에 "AI 업무 자동화" 뱃지가 붙어 있었습니다. 부가세를 찾아 들어온 독자에게 이 뱃지는 글 내용과 맞지 않는 정보입니다. 분류를 그대로 찍으면 된다는 가정이 실측으로 반증된 순간이었습니다.
네 가설이 무너진 자리를 나란히 놓으면 공통점이 하나 보입니다. 접기·무작위·태그는 "독자 눈에 어떻게 보이나"만 따지고, 검색엔진과 테스트가 읽는 HTML은 따지지 않았습니다. 뱃지 가설은 설계 문서만 봐서는 틀린 줄 몰랐고, 실제 글을 넣어 화면을 띄운 뒤에야 드러났습니다. 설계 검토와 실제 렌더 확인을 둘 다 거쳐야 하는 이유가 이 네 줄에 다 들어 있습니다.
진짜 원인은 무엇이었나?
진짜 원인은 두 겹이었습니다. 첫째, 관련 글 목록에 "무엇이 관련 있는가"라는 판단이 아예 없었습니다. 자기 자신을 빼는 것 말고는 아무 기준 없이 전체를 나열하는 구조였습니다. 목록이 길어서 문제가 아니라, 고르는 단계가 없어서 길어질 수밖에 없었습니다. 그래서 가리거나 무작위로 자르는 해법은 원인을 건드리지 못했습니다.
관련 글 목록을 처음 만들 때는 글이 적었고, 전부 보여 주는 것과 골라 보여 주는 것의 차이가 화면에 드러나지 않았습니다. 고르는 규칙을 미룬 판단이 그때는 비용이 없었고, 글 수가 두 배가 되자 비용이 한꺼번에 나타났습니다. 처음 세운 가설들이 모두 "화면을 어떻게 다듬나"에 머문 것도 이 때문입니다. 원인이 화면 아래 데이터 흐름에 있는데 화면에서 답을 찾고 있었습니다.
둘째, 기존 분류 하나가 서로 다른 두 질문에 답하고 있었습니다. 글의 분류값(`reportCategory`)은 "이 글이 어떤 판매 서비스로 독자를 보내는가"를 정하는 값입니다. 그런데 세무·노무 글은 연결할 판매 서비스가 따로 없어서, 업종별 기본값으로 AI 업무 자동화 서비스가 채워져 있었습니다.
이 기본값에는 사연이 있습니다. 2026년 7월 28일, 서비스 연결이 안 된 글 47편(세일즈 인텔리전스 29편·세무 12편·노무 6편)이 이미 판매를 접은 서비스로 독자를 보내고 있는 걸 발견했습니다. 그중에는 네이버 검색 유입이 걸리는 세무 글 2편(48위·75위)도 있었습니다. 그래서 세무·노무 글은 AI 업무 자동화로, 세일즈 인텔리전스 글은 그 후속 서비스로 넘기는 기본값을 두었고, 서비스 링크가 걸리는 글은 9/56에서 56/56이 되었습니다.
독자를 어느 서비스로 보낼지 정하는 데는 이 기본값이 맞았습니다. 하지만 같은 값을 "이 글의 주제"로 화면에 찍자, 부가세 글이 AI 업무 자동화 글처럼 보였습니다. 한 값이 두 질문에 답하면, 한쪽에 맞춘 보정이 다른 쪽에서 오류가 됩니다. 뱃지 오류는 표시 문제가 아니라 분류 축이 하나뿐이어서 생긴 구조 문제였습니다.
조치: 관련 6편 + 허브 1개, 점수로 고른다
낱개 글은 이제 관련 글 6편과 전체 목록 허브(`/reports`) 1개만 가리킵니다. 바퀴 중심에 허브가 있고 낱개 글이 바큇살처럼 붙는 허브 앤 스포크 구조입니다. 사이트 전체를 훑는 역할은 허브가 맡고, 낱개 글은 가까운 이웃만 안내합니다.
숫자 6에 특별한 근거가 있지는 않습니다. 2열 카드로 세 줄이 나오는 분량이라 본문 아래 화면을 넘지 않고, 같은 분류 안에서도 고를 여지가 남는 크기로 골랐습니다. 중요한 건 숫자가 아니라 상한이 있다는 점입니다. 글이 앞으로 몇 배로 늘어도 낱개 글이 거는 링크는 7개에서 멈춥니다.

관련도는 점수로 정합니다. 규칙은 세 줄입니다. 같은 판매 서비스 분류면 +3점, 같은 업종이면 +2점, 겹치는 태그 1개마다 +1점입니다. 점수가 같으면 최신 글이 앞에 옵니다. 전체 목록 함수가 이미 최신순으로 정렬해 돌려주고, 정렬은 같은 값끼리 순서를 바꾸지 않는 방식(stable sort)이라 동점 처리가 따로 필요 없었습니다.

가중치 순서에는 이유가 있습니다. 가장 믿을 수 있는 분류가 가장 무겁고, 검증한 적 없는 태그가 가장 가볍습니다. 태그를 버리지 않고 1점짜리 보조 신호로만 남긴 이유는, 같은 분류 안에서 순서를 가를 때는 쓸모가 있기 때문입니다. 태그만으로 매칭하는 안을 기각한 판단과 어긋나지 않습니다.
규칙대로 부가세 글 하나를 놓고 계산해 보면 순서가 어떻게 갈리는지 보입니다. 다른 세무 글은 판매 서비스 분류가 같고(세무 글은 기본값으로 AI 업무 자동화에 연결돼 있습니다) 업종도 같아 태그 없이도 5점에서 출발합니다. 노무 글은 분류만 같아 3점, 홈페이지 글은 분류도 업종도 달라 태그가 겹칠 때만 점수를 얻습니다. 그래서 부가세 글 아래에는 세무 글이 먼저 채워지고, 모자라면 노무 글, 그래도 모자라면 최신 글이 들어옵니다. 독자가 "이 글 다음에 왜 이 글이 나왔지"라고 물으면 점수 한 줄로 답할 수 있습니다.
점수 0인 글도 후보에서 빼지 않았습니다. 분류가 얇은 글이라도 목록이 비지 않게 하려는 장치입니다. 같은 분류의 글이 6편이 안 되면 남은 자리는 점수 0인 최신 글이 채웁니다. 관련 글 칸이 비어 보이는 페이지가 생기지 않습니다.
화면은 2열 카드로 바꿨습니다. 카드에는 잘리지 않은 제목 전문, 2줄까지 보이는 요약, 주제 뱃지, 날짜가 들어갑니다. 허브의 카드 컴포넌트(`ReportCard`)와 같은 모양을 써서, 독자가 허브에서 본 카드를 글 하단에서도 그대로 알아보게 했습니다.
뱃지는 분류가 아니라 주제로 찍게 했습니다. 주제 라벨을 돌려주는 함수(`reportTopicLabel()`)를 따로 두고, 세무·노무·세일즈 인텔리전스처럼 판매 서비스가 따로 없는 업종 글은 업종 이름을 씁니다. AI 전환 관련 글은 분류 이름을 그대로 씁니다. 이 묶음에만 55편이 있어서, 홈페이지·챗봇·블로그로 나눠 찍어야 카드끼리 구별이 됩니다. 서비스 연결용 값은 그대로 두고, 화면 표시용 값을 새로 만든 셈입니다.
재측정 결과: 102편 전수에서 링크 최대 7개
고친 뒤 같은 방법으로 다시 셌습니다. 빌드한 HTML 102편을 전부 열어 낱개 글이 다른 리포트로 거는 링크를 셌고, 102편 모두 최대 7개(관련 6편 + 허브 1개)였습니다. 101개에서 7개로 내려온 숫자가 한 페이지가 아니라 전수에서 같았습니다.

| 항목 | 고치기 전 | 고친 뒤 |
|---|---|---|
| 낱개 글이 거는 리포트 링크 | 101개 | 최대 7개 (관련 6편 + 허브 1개) |
| 확인 범위 | - | 빌드 HTML 102편 전수 |
| 관련 글 선택 기준 | 자기 자신 제외 전부 | 점수제 (+3 · +2 · 태그 1개당 +1) |
| 제목 표시 | 22자에서 잘림 | 제목 전문 + 요약 2줄 |
| 부가세 글 뱃지 | AI 업무 자동화 (1차 렌더) | 세무 |
코드 쪽 확인도 함께 했습니다. 관련 글 선택 로직에 테스트 12개를 새로 썼고(`src/lib/__tests__/reports-related.test.ts`), 같은 폴더의 테스트 36개가 전부 통과했습니다. 무작위 대안을 기각한 덕에 "이 글 아래에는 이 6편"을 고정값으로 검사할 수 있었습니다. 기각 사유가 그대로 테스트 가능성이 된 셈입니다.
빌드는 914페이지를 문제없이 만들었고, 배포 뒤 실제 사이트에서 2페이지를 열어 렌더를 눈으로 확인했습니다. 그중 하나가 뱃지 오류를 처음 잡은 부가세 글(`/reports/tax-vat-input-non-deduction-2026`)입니다. 이 페이지 카드의 뱃지가 AI 업무 자동화가 아니라 세무로 찍히는 것까지 봤습니다.
독자가 다른 글로 넘어가는 경로가 사라진 것도 아닙니다. 전체 목록이 필요한 독자는 허브로 갑니다. 허브는 같은 날 먼저 붙인 분류 탭과 페이지 나누기를 이미 갖고 있어서, 102편을 한 화면에 쏟지 않고도 전체를 훑을 수 있습니다.
남는 한계는 무엇인가?
이번 조치로 해결하지 못한 것, 그리고 아직 재지 못한 것을 그대로 남깁니다.
- 검색 성과는 아직 재지 않았다. 색인·크롤 영향은 4주 지표에서 읽기로 했고, 이 글을 쓴 자료에는 그 결과 수치가 없다. 링크 수는 줄었지만 검색 순위가 오른다는 증거는 아직 없다
- 허브가 유일한 전체 목록이 됐다. 허브 페이지가 깨지면 낱개 글 사이 이동 경로는 6편짜리 이웃 그래프뿐이라 좁아진다
- 주제 사이 이동이 줄었다. 관련 6편이 같은 분류로만 채워지면 홈페이지 글에서 세무 글로 넘어가는 경로가 사라진다. 주제를 넘나드는 안내는 허브와 상단 메뉴의 몫으로 넘겼다
- 6이라는 숫자와 +3·+2·+1 가중치는 실험으로 찾은 최적값이 아니다. 규칙을 설명할 수 있게 고른 값이다
- 태그 품질은 여전히 검증하지 않았다. 지금은 1점짜리 보조 신호라 피해가 작지만, 태그 비중을 올리려면 먼저 태그를 점검해야 한다
주제 사이 링크를 줄인 판단에는 근거가 하나 더 있습니다. 홈페이지 글을 읽던 독자를 세무 글로 보내면, 구매 의도가 다른 독자를 엉뚱한 곳으로 끌고 가는 쪽에 가깝습니다. 그래도 이건 판단이지 측정이 아니라서, 4주 지표가 나오면 다시 봐야 합니다.
4주 뒤 판단에 쓸 계측은 미리 깔려 있습니다. 2026년 8월 7일, 리포트 글에서 판매 서비스 페이지로 가는 버튼 2개에 클릭 이벤트를 붙였습니다. 그전까지는 검색 콘솔 28일 기준 서비스 페이지 검색 클릭이 0이라는 숫자만 있었고, 그게 "글이 안 읽혔다"인지 "읽혔는데 버튼을 안 눌렀다"인지 가를 수단이 없었습니다. 관련 글을 6편으로 줄인 뒤 독자가 다음 글이나 서비스 페이지로 넘어가는지는 이 클릭 기록으로 따로 확인해야 합니다. 링크 수를 줄인 결과가 검색엔진에도, 독자에게도 이득인지는 두 숫자를 다 봐야 말할 수 있습니다.
관련 글 링크를 다시 설계한다면 무엇부터 확인할까?
이번 일에서 남은 확인 순서를 다섯 줄로 정리합니다. 글이 수십 편을 넘어가는 블로그·콘텐츠 사이트라면 같은 순서를 그대로 쓸 수 있습니다.
- 화면 말고 빌드한 HTML에서 링크를 센다. 접어 둔 링크도 링크다
- 한두 페이지가 아니라 전수로 센다. 최댓값이 기준선 안에 드는지 본다
- 관련 글은 "고르는 규칙"이 있어야 한다. 규칙은 검증된 분류를 무겁게, 검증 안 된 신호를 가볍게 둔다
- 결과가 빌드마다 같아야 한다. 무작위는 테스트도 색인도 흔든다
- 화면에 찍는 값이 원래 어떤 질문에 답하는 값인지 확인한다. 한 값이 두 질문에 답하면 한쪽에서 오류가 난다
글 수가 52편에서 102편이 되는 동안 코드는 한 줄도 바뀌지 않았는데 결과는 계속 나빠졌습니다. 콘텐츠를 쌓는 사이트는 글 수가 늘 때마다 링크 구조를 다시 세어 봐야 합니다. 사이트 구조나 콘텐츠 운영을 점검하고 싶다면 상담에서 상황을 알려 주세요.