DOCS/journey/plans/250906_news_system_integration.md
Claude-51124 fa3a24270c docs: 뉴스 게시 이력 조회 API 구현 완료 문서화
- troubleshooting/260127_news_history_api_implementation.md 추가
- plans/250906_news_system_integration.md 업데이트 (완료 표시)
2026-01-27 09:38:26 +09:00

7.1 KiB

뉴스 시스템 통합 계획

작성일: 2025-09-06 목적: skill-news, rb8001, skill-publish 통합 연동


1. 아키텍처

서비스 구조

APScheduler → skill-news:8505 → rb8001 → Slack 채널
                                    ↓
                            사용자 버튼 클릭
                                    ↓
                            skill-publish:8511 → Squarespace

데이터 흐름

  • 수집: skill-news (Google News API)
  • 요약: Gemini LLM
  • 전송: skill-slack (Block Kit)
  • 게시: skill-publish (Squarespace CMS)

2. 구현 완료

→ 상세: troubleshooting/250907_company_x_news_zero_articles.md 외 다수


3. 구현 완료

→ 상세: troubleshooting/260127_news_history_api_implementation.md


4. 남은 작업

없음 (Phase 3 완료)

목표: 사용자가 게시한 뉴스 이력을 조회할 수 있는 API 제공

현재 상태:

  • rb_news 테이블 생성 완료 (troubleshooting/250920_news_db_persistence_implementation.md)
  • DB 저장 로직 완료 (news_posting_skill.py:save_news_to_db(), update_news_published_status())

기존 코드 수정 필요:

  • rb_news 테이블: slack_user_id VARCHAR(100) 컬럼 추가 (conversation_log 패턴과 일치, UUID 변환 실패 시 원본 보존)
  • news_posting_skill.py:update_news_published_status(): selected_by_user_id(UUID), selected_at, slack_user_id(VARCHAR) 필드 업데이트 추가
  • news_posting_skill.py:handle_publish_action(): Slack ID → UUID 변환 로직 추가 (get_user_uuid_by_slack_id() 사용)

필요 작업 (계층 분리 원칙 준수):

3.1 DB 스키마 수정

  • ALTER TABLE rb_news ADD COLUMN IF NOT EXISTS slack_user_id VARCHAR(100) 실행
  • 목적: UUID 변환 실패 시 원본 Slack ID 보존 (conversation_log 패턴과 일치)
  • 인덱스: CREATE INDEX IF NOT EXISTS idx_rb_news_slack_user_id ON rb_news(slack_user_id) (선택)

3.2 Repository 계층 (app/state/repositories/news_repository.py)

  • 파일 신규 생성
  • get_news_history() 메서드: user_id, 기간, 상태 필터링, 페이지네이션 지원
  • 쿼리 조건:
    • selected_by_user_id = user_id (필수, UUID로 조회, conversation_log 패턴과 일치)
    • is_published = TRUE (기본, status 파라미터로 변경 가능)
    • published_at BETWEEN start_date AND end_date (기간 필터, 선택)
  • 정렬: published_at DESC (최신순)
  • 페이지네이션: LIMIT/OFFSET 사용
  • 반환: Dict (items: List[Dict], total: int, page: int, limit: int, total_pages: int)
  • 항목 필드: id, title, url, summary, published_at, is_published, squarespace_post_id, slack_channel_id, slack_user_id

3.2 Schema 계층 (app/schemas/news_schema.py)

  • app/schemas/ 폴더 신규 생성 (없는 경우)
  • 파일 신규 생성
  • NewsHistoryResponse: 응답 스키마 (items, total, page, limit, total_pages)
  • NewsHistoryItem: 개별 뉴스 항목 스키마 (id, title, url, summary, published_at, is_published, squarespace_post_id)

3.3 Service 계층 (app/services/news_service.py)

  • 파일 신규 생성
  • get_user_news_history() 메서드: Repository 호출, 비즈니스 로직 처리
  • user_id 검증 (UUID 형식), 기본값 설정 (limit=20, page=1, max_limit=100)

3.4 Router 계층 (app/router/news_endpoint.py)

  • 파일 신규 생성
  • GET /api/news/history 엔드포인트
  • 쿼리 파라미터:
    • page (int, 기본값 1): 페이지 번호
    • limit (int, 기본값 20, 최대 100): 페이지 크기
    • status (str, 선택): 필터링 (published, pending 등)
    • start_date (str, 선택): ISO 8601 형식 시작일
    • end_date (str, 선택): ISO 8601 형식 종료일
  • 인증: Depends(get_current_user) - current_user의 게시 이력만 조회 (user_id 파라미터 제거, 보안)
  • Service 호출 → Schema로 응답 변환
  • 에러 처리: 400 (잘못된 파라미터), 401 (인증 실패), 500 (서버 오류)

3.5 Router 등록 (main.py)

  • news_endpoint.router 등록

3.6 기존 코드 수정 (app/services/skills/news_posting_skill.py)

  • handle_publish_action() 메서드 수정: Slack ID → UUID 변환 로직 추가
    • from app.state.repositories.user_repository import get_user_uuid_by_slack_id import
    • user_id.startswith("U") 체크 후 get_user_uuid_by_slack_id(user_id) 호출
    • 변환 성공 시 UUID 사용, 실패 시 원본 Slack ID 보존
  • update_news_published_status() 메서드 수정: user_id(UUID), slack_user_id(VARCHAR) 파라미터 추가
  • UPDATE 문에 selected_by_user_id = user_id, selected_at = NOW(), slack_user_id = slack_user_id 추가
  • 호출부 수정: handle_publish_action()에서 UUID와 원본 Slack ID 모두 전달

3.7 테스트

  • tests/test_news_history.py 작성
  • 실제 DB 조회 테스트 (컨테이너 내부 실행)
  • curl로 API 테스트: GET /api/news/history?page=1&limit=20

원칙 준수 체크리스트:

  • 계층 분리: router → services → state/repositories (311_백엔드_구조_원칙.md 섹션 1)
  • Repository 패턴: DB 접근은 state/repositories에서만 (섹션 6)
  • Schema 분리: API 요청/응답은 schemas로 (섹션 3)
  • 파일 크기: 각 파일 500줄 이하 (섹션 7)
  • 실제 테스트: 추측하지 말고 curl/DB 직접 확인 (섹션 18)
  • 로깅: 시작/종료 INFO, 중간 과정 DEBUG (섹션 11)
  • UUID 사용: user_id는 UUID string으로 처리 (AGENTS.md)

데이터 원칙:

  • 더미 데이터 금지: 실제 rb_news 테이블 데이터만 사용 (섹션 6)
  • JSONB 처리: metadata 필드 사용 시 json.dumps() 필수 (섹션 6)
  • DB 스키마 확인: rb_news 테이블 실제 구조 확인 후 구현 (섹션 6-1)
  • 이중 저장 패턴: selected_by_user_id(UUID)와 slack_user_id(VARCHAR) 함께 저장 (conversation_log 패턴 준수)
  • UUID 변환 실패 처리: 변환 실패 시 selected_by_user_id는 NULL, slack_user_id에 원본 저장

4. 환경 설정

rb8001 (.env)

COMPANY_X_NEWS_CHANNEL_ID=C09CP4MDX71
SKILL_NEWS_URL=http://localhost:8505
SKILL_PUBLISH_URL=http://localhost:8511

Slack App


5. 참고 문서

구현 관련

  • troubleshooting/250907_company_x_news_zero_articles.md
  • troubleshooting/250908_skill_news_companyx_data_integration.md
  • troubleshooting/250908_slack_interactive_gateway_proxy.md
  • troubleshooting/250920_news_db_persistence_implementation.md

원칙 문서

  • book/300_architecture/311_백엔드_구조_원칙.md - 계층 분리, Repository 패턴, 테스트 원칙
  • book/300_architecture/312_문서_작성_원칙.md - 문서 작성 규칙
  • book/300_architecture/database/tables.md - rb_news 테이블 스키마 참고

참고 코드

  • app/router/history_endpoint.py - 페이지네이션 구현 예시
  • app/state/repositories/user_repository.py - Repository 패턴 예시
  • app/services/skills/news_posting_skill.py:503-575 - 기존 DB 저장 로직 참고