발견은 추론 문제가 아니라 배관 문제다
매체마다 공개 피드와 검색 방식이 다르고, 접근 정책과 이용조건도 다릅니다. RSS, 검색 페이지, 공식·공개 API 등 허용된 경로를 소스별 설정으로 관리했습니다. 어떤 매체가 어느 경로인지는 추론할 수 있는 정보가 아니라 한 번 확인해서 적어두는 설정값입니다. 그렇게 정리하니 112개 매체 중 80개는 별도 코드 없이 피드 URL 설정만으로 연결됩니다.
×키워드 하나로 100개 언론사를 LLM에게 맡기는 방식은 작동하지 않습니다. 정확도도, 비용도.
TikTok Korea PR팀의 출발점은 수작업 스프레드시트였습니다. 매체명, 기자명, 기사 제목, 링크를 한 줄씩 채워 넣은 시트가 석 달치 500행을 넘어가고 있었죠. 문제는 노동량만이 아니었습니다. 언론사마다 기사를 공개하는 방식이 전부 달라서 — 어디는 RSS, 어디는 자체 검색 페이지, 어디는 공개 API — "한 번에 다 모으는" 방법이 존재하지 않았습니다.
가장 먼저 떠오르는 해법은 LLM에게 키워드를 주고 알아서 찾아오게 하는 것입니다. 저희도 그 경로를 먼저 검토했고, 세 지점에서 무너진다는 결론에 도달했습니다.
매체마다 공개 피드와 검색 방식이 다르고, 접근 정책과 이용조건도 다릅니다. RSS, 검색 페이지, 공식·공개 API 등 허용된 경로를 소스별 설정으로 관리했습니다. 어떤 매체가 어느 경로인지는 추론할 수 있는 정보가 아니라 한 번 확인해서 적어두는 설정값입니다. 그렇게 정리하니 112개 매체 중 80개는 별도 코드 없이 피드 URL 설정만으로 연결됩니다.
기자 이름 하나를 뽑는 데 네 단계 폴백이 돕니다 — JSON-LD → meta → 본문 추출기 → byline 계열 클래스. 거기에 ‘선임기자’가 ‘기자’보다 먼저 잘리도록 직함 접미사 20종을 최장 일치 우선으로 처리합니다. 정규식은 공짜고, 매번 같은 답을 냅니다. LLM은 둘 다 아닙니다.
기사가 실제로 그 키워드를 다루는지 판단하는 일을 LLM에 넘기면, 발견된 모든 후보에 대해 모델을 불러야 합니다. 저희는 robots 게이트, 목록 사전 필터, 날짜 창, 중복 제거, 본문 최종 확인까지 전부 Python으로 거른 뒤 살아남은 것만 모델에 넘깁니다.
실제 운영 기록입니다. 키워드 하나에 대해 108개 수집 작업을 돌린 결과 — 발견된 655건 중 모델을 만난 것은 345건이었습니다. 나머지는 토큰을 한 개도 쓰지 않고 걸러졌습니다.
각 언론사의 자체 RSS/피드와 네이버 공식 뉴스 검색 API로 후보를 모읍니다.
robots.txt 확인, URL 정규화 후 해시로 중복 제거, 조회 기간 밖 기사 제외.
목록의 제목만으로는 부족합니다. 본문을 받아 정제한 뒤 전문에서 키워드를 다시 확인합니다.
유료 구간은 여기 하나뿐입니다 — 발견 대비 52.7%.
한 번의 호출이 감성 · 요약 · 분류 · 태그를 한꺼번에 돌려줍니다. JSON mode로 형식을 고정하고, max_tokens=300으로 출력을 묶고, 본문은 8,000자에서 자릅니다. 결정적으로 — 모델은 본문을 되돌려주지 않습니다. 기사가 아무리 길어도 출력 비용은 일정합니다.
시스템 전체에서 쓰는 모델은 gpt-4o-mini 하나입니다. 대형 모델은 없습니다. 모델이 뱉은 값도 그대로 믿지 않고, 감성은 3개 값으로, 분류는 9개 값으로 코드에서 다시 고정합니다.
수집 작업은 큐에 쌓이고 워커가 하나씩 임대해 처리합니다. 워커가 죽으면 리퍼가 작업을 회수해 다시 큐에 넣습니다. 매체별 요청 간격, 재시도, 보관 기간 만료는 스케줄러가 관리합니다. Azure Functions와 Container Apps 위에 올린 이 배관이 없으면, 아무리 좋은 모델을 붙여도 100개가 넘는 매체를 매일 안정적으로 돌릴 수 없습니다.
뉴스 모니터링에서 "어떻게 모으는가"는 기술 문제이자 신뢰 문제입니다. 저희는 이 부분을 나중에 붙이는 정책이 아니라 처음부터 코드로 강제했습니다.
연결된 매체 대부분(112곳 중 89곳)은 언론사가 직접 공개한 RSS/피드로 붙입니다. 배포를 전제로 열어둔 채널입니다.
과거 기사 탐색에는 네이버 공식 뉴스 검색 API를 씁니다. 검색 결과 페이지를 긁지 않고, 응답 본문도 저장하지 않습니다.
API로 발견한 기사도 본문은 해당 언론사 URL에서 직접 받아옵니다. 그 매체의 robots 정책·요청 지연·추출기가 그대로 적용됩니다.
호스트별 robots.txt를 캐시해 따르고, 일시적 조회 실패 시에는 수집하지 않는 쪽으로 판단합니다. 예외는 클라이언트가 검토·승인한 경우에 한해 매체 단위로만 적용하며, 모든 예외는 플래그로 기록되어 감사할 수 있습니다.
봇 이름과 연락 가능한 이메일을 UA에 넣습니다. 호스트당 기본 1.5초 간격을 두고, 429/5xx 시 서버가 지정한 Retry-After를 존중합니다.
본문은 180일 후 삭제, 기사 행은 365일 후 완전 삭제됩니다. 사내 전용 도구이며 재배포하지 않습니다.
언론사 111곳을 키워드 단위로 상시 추적 — 30분 / 6시간 / 24시간 주기를 키워드마다 따로 설정
수작업 스프레드시트를 자동 엑셀 내보내기로 대체 — 화면에서 적용한 필터 그대로, 고정된 11개 컬럼
제목과 본문 전체에 대한 불리언 전문 검색 (틱톡 AND 광고 OR 커머스)
기사마다 한국어 요약 · 감성 · 분류 · 태그 자동 부착
매체별 수집 상태 대시보드 — 어느 언론사가 언제부터 안 들어오는지 즉시 확인
108개 수집 작업 중 107건 성공 + 1건 부분 성공 — 작업 단위 재시도와 자동 복구 내장
모델 성능 경쟁은 계속될 것이고 내년이면 지금보다 나은 모델이 나올 겁니다. 그런데 반복적으로 돌아가는 업무에서는, 모델을 바꾸는 것보다 데이터 파이프라인과 인프라를 제대로 설계하는 쪽이 훨씬 큰 차이를 만듭니다.
이 시스템에서 모델을 더 큰 것으로 바꿔도 결과는 거의 달라지지 않습니다. 반대로 앞단의 파이프라인을 걷어내면 시스템 자체가 성립하지 않습니다.