DOCS/troubleshooting/250905_happybell80_naver_rss_playwright_0900_slack_delivery.md

4.5 KiB

평일 09:00 Slack 뉴스 전달을 위한 네이버 RSS+Playwright 수집 설계

작성일: 2025-09-05

상태: 해결방안 확정·구현 계획 수립

목표

  • 평일 09:00에 컴퍼니엑스 Slack 채널과 각자 DM으로 당일 스타트업 뉴스를 전송한다.
  • 소스는 네이버 블로그 ‘깡프로 스타트업 트렌드 연구소’의 당일 포스트다.

문제 요약

  • RSS: https://rss.blog.naver.com/startupventure.xml
  • RSS description이 약 8개 항목에서 절단되어 본문(20개+)이 누락된다.
  • 실제 본문은 블로그 페이지 내 PostView.naver iframe에 포함되어 정적 파싱으로는 불완전하다.

최소 구성(가장 단순)

  • 목적: 저장을 생략하고, 09:00에 “제목+링크 목록(Slack 텍스트/JSON)”만 생성해 상위가 전송.
  • 입력: ENV NAVER_RSS_URL(기본값), 요청 date(YYYY-MM-DD)|today, format(json|slack).
  • 처리: RSS→당일 포스트 URL→Playwright로 PostView.naver iframe 렌더링→제목+원문 링크(+순서) 추출.
  • 출력: JSON {success,count,items:[{title,url,position}]} 또는 Slack 텍스트(• <url|title> 리스트).
  • 저장: 기본 비저장(가장 단순). 옵션 STORE_MINIMAL=true 시 Chroma에 최소 메타 저장(idempotent 보조).

확정된 사실(코드/실측)

  • skill-news 서비스 포트: 8505 (/health, /api/news/...).
  • Endpoints: /api/news/search, /latest, /summarize, /status, /process/all, /search/similar.
  • 데이터 영속: ChromaDB CHROMA_DB_PATH=/app/data/chroma, 컬렉션 news_articles.
  • Playwright 사용 가능(Chromium headless, iframe 내 본문 접근 검증 완료).

해결 전략(하이브리드)

  1. RSS로 당일 포스트 URL만 획득한다.
  2. 해당 포스트의 PostView.naver iframe을 Playwright로 렌더링하여 리스트형 항목의 제목과 기사 원문 링크를 모두 추출한다.
  3. 추출 항목을 중복 제거·정규화 후 최소 메타데이터로 ChromaDB에 저장(재실행 안전성 확보).
  4. Slack 포맷 텍스트( <url|title> 리스트 )와 JSON을 모두 제공한다.
  5. 상위 로빙/슬랙봇이 채널/DM 전송을 담당(HTTP API만으로 연동).

API 설계(HTTP 전용)

  • POST /api/news/naver/fetch-headlines (최소 경로)
    • 요청: { "date": "YYYY-MM-DD", "source": "startupventure", "format": "json|slack" }
    • 응답(JSON): { success, count, items: [{title, url}], message? }
    • 응답(Slack): text 필드에 불릿 리스트 문자열 포함.
  • GET /api/news/naver/latest
    • 당일/최근 포스트 기준 재생성 없이 캐시된 결과 반환.

파싱 규칙(함수형·환경주입)

  • 입력: rss_url, date, max_items, timeout은 ENV → 요청값으로 오버라이드.
  • 출력: 순수 데이터(제목, URL) 리스트. 상태 저장은 호출자(데이터 매니저)가 수행.
  • 셀렉터 전략: 번호형 텍스트 패턴 탐지 + 앵커 태그의 절대 URL 보정.
  • 예외: 네트워크/구조 변경 시 빈 리스트와 원인 메시지 반환(부작용 없음).

스케줄링/흐름(평일 09:00)

  1. 스케줄러(Gitea Actions 혹은 상위 컨트롤러)가 09:00에 POST /api/news/naver/fetch 호출.
  2. 응답을 받아 채널용 메시지와 DM용 메시지를 구성(상위 로직).
  3. Slack 전송 성공/실패 로그를 상위에서 저장. skill-news는 수집·정규화에 집중.

운영 포인트

  • 재시도: 3회(지수 백오프), 실패 시 캐시 결과 반환.
  • 속도제한: 항목 간 sleep(0.5~1.0s)로 요청 간격 조절.
  • 중복: 제목+URL 해시로 식별, 이미 저장된 항목은 건너뜀.
  • 관찰성: /api/news/status에 수집/요약 카운터와 최신 시각 반영.

검증 시나리오(curl)

  • 수집: curl -X POST http://localhost:8505/api/news/naver/fetch -H 'Content-Type: application/json' -d '{"date":"2025-09-05","source":"startupventure","format":"slack"}'
  • 상태: curl http://localhost:8505/api/news/status

리스크 및 대응

  • 네이버 마크업 변경: 셀렉터 다중화, 본문 fallback(전체 body 텍스트) 적용.
  • 일시적 차단: User-Agent/Referer 설정과 백오프 재시도.
  • RSS 지연: 당일 포스트 미등재 시 전일 18시~당일 09시 범위 탐색 후 빈 결과 처리.

다음 단계

  • naver/fetch 엔드포인트 구현(함수형), 데이터 매니저에 아이템 최소 저장 로직 보강.
  • 슬랙 포맷 메시지 빌더 추가 또는 기존 format=slack 경로 재사용.
  • Gitea 스케줄 워크플로에 09:00 평일 트리거 추가(롤백·로그 포함).