# 깡프로 뉴스 용어 추출 기능 추가 (2025-09-14) ## 요구사항 매일 9시 10분 깡프로 뉴스(네이버 스타트업 헤드라인)에 "오늘 알아둘 용어" 섹션 추가 ### 목표 구조 ``` 오프닝 메시지 ↓ 출처 링크 ↓ 헤드라인 목록 (01-50번) ↓ 오늘 알아둘 용어 (NEW) ↓ 로빙에게 물어보기 CTA (NEW) ↓ 클로징 메시지 ↓ 명언 ``` ## 현재 상태 분석 ### 1. 수정 후 처리 흐름 (startup_news_skill.py 추가) 1. `rb8001/main.py:206` - APScheduler 스케줄 등록 2. `rb8001/main.py:213` - run_headlines_job 호출 (import 변경) 3. `rb8001/app/skills/startup_news_skill.py:run_headlines_job()` - 메인 실행 4. `rb8001/app/skills/startup_news_skill.py:fetch_headlines()` - 내부 메서드 5. `rb8001/app/commands/skill_commands.py:220` - fetch_naver_headlines(json) 6. `skill_news/app/api/news_endpoints.py:46` - POST /naver/startup-headlines 7. `skill_news/app/services/naver_startup_news_service.py:360` - fetch_headlines() 8. `rb8001/app/skills/startup_news_skill.py:extract_terms()` - 용어 추출 9. `rb8001/app/llm/gemini_handler.py:215` - extract_keywords() 호출 10. `rb8001/app/skills/startup_news_skill.py:build_message()` - 메시지 조립 11. `rb8001/app/skills/startup_news_skill.py:send_to_slack()` - 자체 Slack 전송 ### 2. 문제점 (구조적) - **main.py 과부하**: 스케줄/실행/전송 모두 담당 (732줄) - **역할 혼재**: skill-news가 데이터 수집+Slack 포맷팅까지 담당 - **응집도 낮음**: 하나의 기능이 8개 파일에 분산 - **왕복 낭비**: rb8001→skill-news(포맷)→rb8001(전송) - **용어 추출 불가**: 완성된 텍스트라 중간 삽입 어려움 ## 구현 방안 (최종 선택) ### startup_news_skill.py 신규 생성 ```python # rb8001/app/skills/startup_news_skill.py import os import logging from typing import List, Dict from slack_sdk import WebClient from app.core.config import settings from app.commands.skill_commands import SkillCommands from app.llm.gemini_handler import GeminiHandler logger = logging.getLogger(__name__) async def run_headlines_job(channel_id: str): # 1. JSON 포맷으로 헤드라인 수집 sc = SkillCommands() result = await sc.fetch_naver_headlines(fmt="json") items = result.get("items", []) # 2. 용어 추출 (환경변수 체크) extract_terms = os.getenv("HEADLINES_EXTRACT_TERMS", "true").lower() == "true" if extract_terms and items: # 헤드라인 제목들 추출 titles = [item["title"] for item in items] # LLM으로 용어 추출 terms_prompt = f""" 다음 스타트업 뉴스 헤드라인에서 비즈니스맨이 알아둬야 할 핵심 용어 {os.getenv("HEADLINES_TERMS_COUNT", "5")}개를 추출해줘. 전문용어, 신조어, 트렌드 키워드 위주로. 헤드라인: {chr(10).join(titles[:20])} # 상위 20개만 응답 형식: 용어1, 용어2, 용어3 """ handler = GeminiHandler() terms = await handler.extract_keywords(terms_prompt, max_keywords=5) # 3. skill-news에서 slack 포맷 가져오기 slack_result = await sc.fetch_naver_headlines(fmt="slack") text = slack_result.get("text", "") # 4. 용어 섹션 삽입 (클로징 전에) if extract_terms and terms: lines = text.split("\n") # 클로징 찾기 (빈 줄 2개 다음) insert_idx = -1 for i in range(len(lines)-2, 0, -1): if lines[i] == "" and lines[i-1] == "": insert_idx = i break if insert_idx > 0: terms_section = [ "", f"*오늘 알아둘 용어*: {terms}", "", "궁금한 용어가 있으신가요? *로빙에게 물어보세요!*", "" ] lines[insert_idx:insert_idx] = terms_section text = "\n".join(lines) # 5. 자체 Slack 전송 (Company-X 봇 토큰 사용 - 같은 채널) slack_client = WebClient(token=os.getenv("COMPANYX_SLACK_BOT_TOKEN")) slack_client.chat_postMessage(channel=channel_id, text=text) ``` ### 환경변수 추가 ```bash # rb8001/.env HEADLINES_EXTRACT_TERMS=true # 용어 추출 기능 활성화 HEADLINES_TERMS_COUNT=5 # 추출할 용어 개수 HEADLINES_TERMS_SAMPLE_SIZE=20 # 분석할 헤드라인 개수 ``` ## 구현 체크리스트 - [x] startup_news_skill.py 생성 - [x] main.py에서 기존 코드 제거 (-50줄) - [x] Mistral API 통합 (컨텍스트 없는 용어 추출) - [x] 용어 섹션 위치 조정 (클로징 후, 명언 전) - [x] 프롬프트 개선 및 번호 제거 처리 ## 예상 결과 ``` 안녕하세요, 로빙입니다. 오늘 스타트업 헤드라인만 모았어요. 출처: 깡프로 스타트업 뉴스 01-50. [헤드라인 목록] *오늘의 키워드*: 홈런펀드, 식물성콜라겐, GPU클러스터 궁금한 용어가 있으신가요? *로빙에게 물어보세요!* [클로징 및 명언] ``` ## 구현 영향 - **main.py**: -50줄 삭제, +1줄 import (732 → 683줄) - **startup_news_skill.py**: +150줄 신규 생성 - **gemini_handler.py**: 프롬프트 수정 불필요 (파라미터로 전달) - 기존 extract_keywords() 함수 활용 (미사용 코드 재활용) ## 교훈 - GeminiHandler.chat()은 사용자 컨텍스트 포함 → 용어 추출 오염 - Mistral API 직접 호출로 순수 텍스트 처리 가능 - 리스트→문자열 변환 시 join() 필수, 타입 체크 중요