URL-to-Markdown 추출기가 2,000단어 기사에 단 340자만 반환한 문제 — 페이지가 JavaScript로 자체 렌더링된 경우
요약
웹 페이지에서 마크다운으로 콘텐츠를 추출하는 파이프라인을 운영하던 중, 클라이언트 측 렌더링(JavaScript)된 페이지에서 본문 누락 문제를 경험했습니다. 이를 해결하기 위해 '추출 문자 수 대비 원본 HTML 총 텍스트 문자 수' 비율 추적 기능을 추가하여 낮은 커버리지를 감지하고, 자동으로 헤드리스 브라우저를 이용한 재시도(rendered fetch) 폴백을 구현했습니다.
핵심 포인트
- 클라이언트 측 렌더링 페이지는 본문이 누락될 위험이 높습니다.
- 추출 수율 측정은 SPA와 일반 페이지를 구분하는 효과적인 방법입니다.
- 수율이 낮으면 헤드리스 브라우저로 재시도하는 폴백 로직을 적용해야 합니다.
- 느린 렌더링 과정으로 인해 빠른 경로가 우선권을 유지하도록 설계했습니다.
제 변환 파이프라인을 운영한 지 몇 주 만에 이상한 종류의 실패를 경험했습니다. 어떤 기사는 완벽하게 처리되었지만, 다른 기사들은 내비게이션 텍스트 조각만 반환하고 아무것도 없었습니다.
원 페이지를 가져와 보니 모든 것을 알 수 있었습니다. 기사 본문이 HTML 안에 아예 없는 것이었습니다. 비어있는 <div id="root""></div>과 JavaScript 번들만 있을 뿐이었습니다. 즉, 페이지가 클라이언트 측에서 자체 렌더링된 것입니다. 제 텍스트 밀도 휴리스틱은 존재하는 블록만 점수를 매길 수 있습니다. 본문이 누락되면, 보통 쿠키 알림이나 푸터와 같은 그곳에 있는 가장 큰 텍스트를 가져옵니다.
그래서 어떤 폴백(fallback)을 추가하기 전에, 저는 수율 추적 기능을 추가했습니다:
- 추출된 문자 개수
- 원본 HTML의 총 일반 텍스트 문자 개수
- 이 둘 사이의 비율
테스트한 페이지들 전반에 걸쳐, 약 5% 미만의 커버리지는 거의 항상 클라이언트 렌더링 사이트였습니다. 이 단 하나의 비율이 URL 패턴 추측(소스에서 "react" 등을 확인하는 것 — 잡음이 많고 오탐지율이 높습니다)보다 SPA 셸과 일반 페이지를 훨씬 더 잘 구분해 주었습니다.
수정 사항은 기본값이 아니라 폴백이 되었습니다. 추출 수율이 낮으면, 서비스는 렌더링된 가져오기(rendered fetch)로 재시도합니다. 헤드리스 브라우저가 네트워크가 안정될 때까지 기다린 다음, 렌더링된 DOM을 동일한 추출기에 전달하고 일반 파이프라인이 계속됩니다. 그 블로그 게시물의 경우, 출력은 340자에서 약 10,800자로 증가했습니다.
실제 트레이드오프(tradeoffs)가 있습니다. 렌더링 패스는 수백 밀리초가 아니라 몇 초가 걸리기 때문에, 빠른 경로가 우선권을 유지하며 모든 응답에는 호출자가 어떤 경로로 서비스를 받았는지 알 수 있도록 rendered: true 플래그가 포함됩니다. 로그인 뒤에 있거나 공격적인 봇 검사가 있는 사이트는 여전히 실패합니다 — 저는 조각(stub)을 반환하기보다는 정직한 오류를 반환하는 편이 낫습니다.
결국 저는 두 경로 모두를 URL-to-Markdown API(https://x402.freeq.one/tools/markdown.html)에 패키징했습니다. 하지만 이 교훈은 모델에 데이터를 공급하는 모든 가져오기 파이프라인에 적용됩니다: 추출 수율을 측정하고, 의심스러울 정도로 낮으면 콘텐츠가 그렇지 않다는 것이 증명될 때까지 JavaScript에 의해 구축되었다고 가정해야 합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기