Blog Writing Automation System
RSS 뉴스를 수집하여 Gemini AI로 한국어 데일리 브리핑 블로그 포스트를 자동 생성하는 시스템.
Tech Stack
- Backend: Python 3.11
- AI: Google Gemini (2.5 Flash → 2.0 Flash → 2.0 Flash Lite fallback)
- Frontend: Jekyll 4.3 (커스텀 테마 — 외부 테마 gem 미사용), GitHub Pages
- 폰트: Pretendard (jsDelivr CDN), 다크모드 지원 (OS 설정 추종 + 수동 토글)
- CI/CD: GitHub Actions (하루 4회 자동 실행: 08/13/18/23시 KST)
- 알림: Telegram Bot (선택) — 실행당 요약 1통 (토픽별 한 줄 요약 + 키워드 + 링크)
프로젝트 구조
scripts/ # Python 핵심 로직
main.py # 오케스트레이션 (fetch → generate → publish)
fetch_news.py # RSS 수집 및 키워드 기반 카테고리 분류
generate_post.py # Gemini API로 블로그 포스트 생성
notify_telegram.py # 텔레그램 알림 (단일 요약 메시지)
config/
rss_feeds.yaml # RSS 피드 소스 및 카테고리/키워드 설정
_posts/YYYY/MM/DD/ # 생성된 블로그 포스트 (날짜별 하위 디렉토리)
_layouts/
default.html # 공통 레이아웃 (헤더/푸터/테마 토글)
post.html # 글 상세 레이아웃
assets/css/
style.css # 전체 스타일 (디자인 토큰 기반, 단일 파일)
index.html # 글 목록 — 토픽 탭 + 날짜 선택 + 시간대별 묶음
categories.html # 카테고리 페이지
tags.html # 태그 페이지
.github/workflows/
daily-blog.yml # 자동 포스트 생성 워크플로우
jekyll-gh-pages.yml # Jekyll 빌드 및 Pages 배포
실행 방법
# 로컬 실행
pip install -r requirements.txt
export GEMINI_API_KEY="your-key"
python scripts/main.py
# Jekyll 로컬 서버
bundle install
bundle exec jekyll serve
환경 변수 (GitHub Secrets)
GEMINI_API_KEY(필수): Google AI Studio API 키TELEGRAM_BOT_TOKEN(선택): 텔레그램 봇 토큰TELEGRAM_CHAT_ID(선택): 텔레그램 채팅 ID
핵심 아키텍처
- 시간 윈도우 기반 수집: 각 실행 시간에 따라 이전 N시간 뉴스 수집 (08시=9h, 13/18/23시=5h)
- 4개 토픽: IT(
it), 주식증시(stock), 경제금융(economy), 정치사회(politics) — 각 토픽별 키워드 기반 서브카테고리 분류 (config/rss_feeds.yaml의topics) - Gemini Fallback: 2.5 Flash → 2.0 Flash → 2.0 Flash Lite 순차 시도, 모델당 3회 재시도 + 지수 백오프
- 자동 발행: 포스트 생성 → git commit → GitHub Pages 자동 배포
포스트 규칙
- 파일명:
YYYY-MM-DD-HHMM-{slug}.md(slug:it-brief,stock-brief,economy-brief,politics-brief) - 저장 경로:
_posts/YYYY/MM/DD/(scripts/main.py의save_post가 날짜 디렉토리 생성) - 커밋 메시지:
📰 {날짜} {시간} 데일리 브리핑 자동 발행 ({N}개) - 콘텐츠: 1500자 이상 한국어, blockquote 요약 + 카테고리별 섹션 + 마무리 문단
프론트엔드 규칙
- 스타일은
assets/css/style.css한 파일에서 관리. 모든 색·여백·라운드는:root의 디자인 토큰을 참조하고, 컴포넌트 규칙에 색상값을 직접 쓰지 않음 (다크모드가 토큰 재정의로만 동작하기 때문) - 다크모드 3상태:
:root(라이트 기본) /@media (prefers-color-scheme: dark)+:not([data-theme="light"])(OS 추종) /:root[data-theme="dark"](토글 수동 선택). 토큰을 추가하면 세 블록 모두에 정의할 것 - 디자인 원칙: 박스·그림자 대신 1px 라인과 여백으로 구분.
box-shadow는 떠 있는 요소(드롭다운, sticky 네비)에만 허용 - 목록 행(
.post-card/.cat-link/.tag-post-link)은 공용 그리드 규칙을 공유하므로 한쪽만 수정하지 말 것 index.html/categories.html/tags.html은 각자 inline<script>로 렌더링하므로, 클래스명 변경 시 해당 JS의 셀렉터도 함께 확인
코딩 규칙
- 언어: Python 코드 내 주석은 한국어
- RSS 피드 추가/변경은
config/rss_feeds.yaml에서 관리 - Gemini API 에러 핸들링 시 503/429 등 서버 에러는 반드시 재시도 로직 포함
_posts/디렉토리의 자동 생성 파일은 직접 수정하지 않음