DOCS/journey/plans/archive/260202_뉴스브리핑_LangGraph_전환.md

4.9 KiB

뉴스 브리핑 LangGraph 워크플로우 전환

날짜: 2026-02-02
작성자: Claude
관련 파일: rb8001/app/services/skills/startup_news_skill.py, rb8001/app/services/workflows/headlines_workflow.py, rb8001/tests/test_headlines_workflow.py
원칙 참조 (구현 전 필수 확인):

  • 311_backend_coding_principles.md: LangGraph 워크플로우 (섹션 5), 계층 분리
  • 312_writing-principles.md: 핵심만 간결, 파일명:줄번호
  • 315_테스트_원칙.md: 테스트는 TDD로 진행 (Red → Green → Refactor)

1. 현재 문제

  • 원칙 위반: 뉴스 브리핑은 수집→필터→번역→포맷→전송 다단계 처리인데, 일반 함수로 구현되어 있음. 311 원칙 "복잡한 다단계 처리는 LangGraph 적극 활용" 위반.
  • 추적성 부족: 각 단계별 실패 지점, 중간 상태, 실행 시간 등이 로그에 체계적으로 남지 않음.
  • 복구 불가: 중간 단계 실패 시 처음부터 재실행해야 함 (체크포인트 없음).

2. LangGraph 전환 설계

2.1 상태 모델 (HeadlinesState)

필드 타입 설명
channel_id str Slack 채널 ID
naver_items List[Dict] 깡프로 헤드라인(json)
naver_slack_text str 깡프로 슬랙 포맷 텍스트
sea_text str 동남아 섹션 텍스트 (없으면 "")
terms List[str] 추출된 용어 (없으면 [])
formatted_text str 최종 Slack 텍스트
message_ts Optional[str] 전송된 메시지 ts
errors List[str] 각 단계 에러
extract_terms bool HEADLINES_EXTRACT_TERMS 결과

2.2 노드 구성 (입출력/에러 규칙 고정)

  1. fetch_naver_node: SkillCommands.fetch_naver_headlines(fmt="json")naver_items (실패 시 errors 추가, naver_items=[])
  2. fetch_sea_node: SkillCommands.fetch_sea_headlines(fmt="json")sea_text (실패 시 errors 추가, sea_text="")
  3. extract_terms_node: HEADLINES_EXTRACT_TERMS=true & naver_items 존재 시 GeminiHandler.extract_keywords()terms (실패 시 errors 추가, terms=[])
  4. format_node: fetch_naver_headlines(fmt="slack")naver_slack_text 확보 후
    • _insert_sea_section_into_headlines_text()sea_text 삽입
    • 기존 용어 삽입 규칙(명언 앞 삽입) 유지 → formatted_text
  5. send_node: WebClient(...).chat_postMessage()message_ts (실패 시 errors 추가)

2.3 워크플로우 인터페이스

  • create_workflow(checkpointer=None) -> CompiledGraph
  • async run_headlines_workflow(channel_id: str, date: Optional[str] = None) -> Dict
    • 반환: {"success": bool, "message_ts": Optional[str], "errors": List[str]}

2.4 라우팅

START → fetch_naver → fetch_sea → extract_terms → format → send → END
  • fetch_sea 실패 시에도 계속 진행 (graceful degradation)
  • extract_terms 환경변수 false면 skip (terms 유지)
  • 각 노드 실패는 errors 리스트에 기록, 워크플로우 중단 금지

2.5 체크포인터

  • AsyncSqliteSaver 사용 (경량 워크플로우, 서버 재시작 시 재개 불필요)
  • DB 경로: settings.LANGGRAPH_STATE_DIR + headlines_graph.sqlite
  • thread_id: f"headlines:{channel_id}:{date}" (date=Asia/Seoul YYYY-MM-DD)

2.6 로깅 기준

  • 노드 시작/완료 시 항목 수/문자 수 로그
  • 오류는 errors 누적 + logger.warning/error

3. Phase별 작업

Phase 1: 워크플로우 파일 생성

  • 파일: rb8001/app/services/workflows/headlines_workflow.py (신규)
  • 내용: HeadlinesState, 5개 노드, StateGraph 정의, create_workflow()/run_headlines_workflow() 제공

Phase 2: startup_news_skill.py 리팩토링

  • 기존: run_headlines_job() 함수 → 변경: LangGraph 워크플로우 호출
  • 로그: 각 노드별 실행 로그 + 최종 message_ts 로그
  • 시그니처 유지: run_headlines_job(channel_id) 유지 (스케줄러 변경 최소화)

Phase 3: 테스트 (TDD 진행, 315_테스트_원칙.md)

  • 파일: rb8001/tests/test_headlines_workflow.py (신규)
  • 시나리오:
    • 정상 플로우 (깡프로 + 동남아 + 용어)
    • 동남아 실패 시 graceful degradation
    • 용어 추출 skip
    • Slack 실패 시 errors 기록
    • message_ts 반환 확인 (성공 케이스)

Phase 4: 배포 및 검증

  • 배포: git push origin main (rb8001) → Gitea Actions 자동 배포
  • 검증: 내일 아침 09:10 스케줄 실행 시 로그에 노드별 실행 + message_ts 확인

4. 남은 작업

  • headlines_workflow.py 생성 (LangGraph StateGraph)
  • startup_news_skill.py 리팩토링 (워크플로우 호출)
  • test_headlines_workflow.py 작성 (TDD 진행)
  • 배포 및 스케줄 실행 검증

참고

  • book/300_architecture/311_backend_coding_principles.md
  • book/300_architecture/312_writing-principles.md
  • book/300_architecture/315_테스트_원칙.md