DOCS/troubleshooting/250905_happybell80_naver_rss_playwright_0900_slack_delivery.md
happybell80 800ce99039 Update: APScheduler 헤드라인 문서 - 운영 확인 완료
- 실제 환경변수 값 기록 (채널 ID, 09:00, mon-fri)
- 상태를 운영 확인으로 변경
- Slack 메시지 길이 제한 이슈 제거
2025-09-06 17:13:52 +09:00

5.4 KiB

평일 09:00 Slack 헤드라인 전달: 네이버 RSS + Playwright 구현 문서

작성일: 2025-09-05

상태: 배포 완료(운영 확인 2025-09-06)

목표/배경

  • 평일 09:00에 컴퍼니엑스 Slack 채널/DM로 당일 스타트업 헤드라인(제목+링크) 자동 전송.
  • 소스: 네이버 블로그 ‘깡프로 스타트업 트렌드 연구소’ 당일 포스트.
  • 문제: RSS description이 중간에서 잘려(약 8개) 본문 전체를 담지 못함 → 원문(PostView iframe) 파싱 필요.

아키텍처(23/24 서버 맥락)

  • 23: 메인(Nginx, Gitea, frontend, auth). 24: 로빙/스킬(rb8001, skill-*).
  • skill-news(24, 8505): 헤드라인 생성 API 제공(HTTP). 저장/전송은 상위에서 담당.
  • rb8001(24, 8001): 스케줄러(테스트 단발, 향후 09:00 등록) + Slack 전송.

최종 엔드포인트(구현됨)

  • POST /api/news/naver/fetch-headlines
    • 요청: { "format": "json|slack" } (기본 json)
    • 응답(json): { success, count, items:[{title,url}], source_post_url }
    • 응답(slack): { success, count, text, items, source_post_url }
    • 동작: RSS→최신 포스트 링크→PostView iframe 렌더링→본문 컨테이너에서 제목/URL 추출→Slack 텍스트 생성

파싱 로직(단순·안정화)

  • 렌더링: Playwright(Chromium, headless)로 PostView 본문 컨테이너 텍스트/HTML 확보.
  • 추출 규칙(HTML 우선):
    • 본문 전체의 모든 a[href]를 순회.
    • URL 검증(urlparse netloc 존재, 내부 뷰어/블로그 제외: postview.naver, blog.naver.com).
    • 제목은 해당 앵커의 가장 가까운 블록(p/li/div/span) 텍스트에서 앵커 텍스트·URL·선행번호 제거 후 생성.
    • 비어 있으면 앵커 텍스트 사용(텍스트가 URL이면 제외). 80자 초과 시 말줄임.
  • 보조(텍스트 기반): HTML 추출 실패 시 텍스트 패턴에서 URL 인접 텍스트로 보조 매칭.
  • 번호: 1..N은 포맷 단계에서 부여(연속번호 제약 제거).

Slack 포맷(멘트/가독성)

  • 헤더(오프너): 기본 “안녕하세요, 로빙입니다. 오늘 스타트업 헤드라인만 모았어요.”
  • 본문: NN. <URL|제목> 행으로 정렬(번호는 01부터)
  • 푸터(클로저): 기본 “가볍게 시작해 힘차게 달려봐요. 로빙이 함께합니다.”
  • 줄 간격: 기본 이중 줄바꿈(ENV로 단일 전환 가능)
  • 출처 표기: 출처: <https://rss.blog.naver.com/startupventure.xml|네이버 깡뉴스>

환경변수(스킬)

  • 필수/기본
    • NAVER_RSS_URL(기본: 깡뉴스 RSS), NAVER_MAX_ITEMS(표시 개수, 무제한은 크게 설정)
    • SCRAPE_TIMEOUT(기본 30초)
  • 포맷팅
    • HEADLINES_OPENER, HEADLINES_CLOSER, HEADLINES_SOURCE_URL
    • HEADLINES_DOUBLE_SPACING(true/false)

환경변수(로빙 rb8001) - 운영 설정 확인됨

  • HEADLINES_TEST_CHANNEL_ID=C0920L68267 (실제 채널)
  • HEADLINES_TEST_RUN_AT=09:00 (KST)
  • HEADLINES_SCHEDULE_DAYS=mon-fri (평일만)
  • HEADLINES_MIN_COUNT=1 (모든 뉴스 전송)
  • SLACK_BOT_TOKEN=xoxb-... (설정됨)

스케줄링/흐름 (APScheduler 적용 완료)

  1. rb8001 시작 시 APScheduler가 cron job 자동 등록(평일 09:00, 서버 재시작 불필요).
  2. 환경변수: HEADLINES_SCHEDULE_DAYS(mon-fri), HEADLINES_TEST_NOW(즉시 테스트).
  3. 관리 API: /api/schedule/list(목록), /api/schedule/test(즉시 실행).

테스트 결과(요약)

  • 배포/헬스: skill-news 8505 정상, rb8001 재기동 후 스케줄 등록 로그 확인.
  • 22:10 실전 전송: 채널로 전송 성공(초기엔 잡음/가독성 이슈 있었음).
  • 개선 과정:
    • 초기: RSS 절단 → 본문 전체 파싱으로 잡음 포함.
    • 정규식 기반 번호/URL 패턴 → 0건 회귀 케이스 발생.
    • HTML 앵커 우선 + 근접 텍스트 제목 생성 → 제목 정상화, 유효 URL만 유지.
  • 현재: 제목이 URL로 표시되던 문제 해결, 표시 개수는 NAVER_MAX_ITEMS로 조정.

운영 가이드

  • 전송 정책: count < 임계치면 운영자 경고 메시지로 대체.
  • 로그: “수집 N건/필터 제외 M건/최종 K건” 요약을 INFO로 기록.
  • 헬스체크: 외부 핑은 TTL 캐시(예: 60초) 또는 /healthz 경량 엔드포인트 병행 권장.
  • 실패 대응: 1→2→4초 백오프 3회 재시도, 최종 실패는 운영 채널 알림.

리스크/제약

  • 네이버 마크업 변경 시 셀렉터/타이밍 재조정 필요 (자동 모니터링 없음).
  • 내부 링크 정책: blog.naver.com, PostView.naver는 제외, naver.me/언론 도메인은 허용.

검증 시나리오

  • Slack 텍스트:
    • curl -X POST http://localhost:8505/api/news/naver/fetch-headlines -H 'Content-Type: application/json' -d '{"format":"slack"}'
  • JSON:
    • curl -X POST http://localhost:8505/api/news/naver/fetch-headlines -H 'Content-Type: application/json' -d '{"format":"json"}' | python3 -m json.tool

롤백/튜닝

  • 롤백: skill-news 최근 커밋 git revert <hash> && git push → Actions로 자동 재배포.
  • 튜닝 포인트: NAVER_MAX_ITEMS, 줄간격, 오프너/클로저, URL 필터(내부 도메인 목록) 등 ENV로 조정.

결론

  • 목표(제목+링크 헤드라인 자동 전송) 달성을 위한 최소·안정 경로를 구현 완료.
  • 파싱은 HTML 앵커 우선, 포맷은 Slack 멘트/번호/간격으로 가독성 확보.
  • 스케줄은 rb8001에서 관리하며, 기능은 HTTP API로만 결합(함수형·무하드코딩 원칙 준수).