내가 만든 AI 에이전트 (5/7) — Blogger API 자동 발행 시스템 구축기

Blogger API v3 + OAuth 2.0으로 마크다운 기반 자동 발행 파이프라인 설계하기


핵심 요약

  • Blogger API v3 + OAuth 2.0으로 마크다운 파일을 게시 가능한 HTML 포스트로 변환·발행하는 파이프라인을 구성할 수 있다
  • blogger_publish.py가 frontmatter 파싱 → body 정리 → HTML 변환 → CSS 주입 → API 게시를 단일 스크립트로 처리한다
  • 대량 게시 시 API 쿼터 관리(딜레이, 재시도, TOC 호출 최소화)가 핵심 설계 요소다

플랫폼 선택 기준

AI 에이전트 기반으로 콘텐츠를 자동 발행하려면 API 접근이 필수다. 플랫폼별 조건을 비교하면:

플랫폼 API 비용 AdSense 판단
WordPress.com REST API 유료 플랜 필요 유료만 비용 부담
Ghost REST API 셀프호스팅 or 유료 직접 설정 관리 부담
Blogger v3 API 무료 기본 통합 채택

Blogger는 무료이면서 REST API를 제공하고, AdSense와 기본 통합된다. 개인 블로그 자동화 용도에서 이 조합이 가장 실용적이다.


본문

1. OAuth 2.0 인증 구조

Blogger API v3는 OAuth 2.0 인증을 요구한다. 전체 흐름은 다음과 같다:

  1. Google Cloud Console에서 OAuth 2.0 클라이언트 ID 생성 → client_secret.json 다운로드
  2. blogger_auth.py에서 InstalledAppFlow로 로컬 서버 인증 실행 (port 8080)
  3. 사용자가 브라우저에서 Google 계정 로그인 + 권한 승인
  4. token.json 생성 — access token + refresh token 포함
  5. 이후 요청에서 refresh token으로 자동 갱신
flow = InstalledAppFlow.from_client_secrets_file(client_secret, scopes)
creds = flow.run_local_server(port=8080)

최초 1회만 브라우저 인증이 필요하고, 이후에는 token.json의 refresh token으로 자동 갱신된다. 에이전트가 사람 개입 없이 게시할 수 있는 기반이다.

토큰 만료 주의: refresh token 자체는 앱이 Google Cloud Console에서 "테스팅" 모드로 등록된 경우 약 7일 후 revoke된다. 장기 운영 시에는 앱을 "프로덕션" 상태로 전환해야 refresh token이 영구 유지된다.

2. blogger_publish.py — 핵심 발행 스크립트

이 스크립트가 자동 발행 파이프라인의 중심이다. 마크다운 파일을 입력받아 Blogger에 게시한다.

처리 흐름:

마크다운 파일 → frontmatter 파싱 → body 정리 → HTML 변환 → CSS 주입 → API 게시

frontmatter 파싱: 마크다운 파일 상단의 --- 블록에서 제목과 라벨을 추출한다. 제목:, 라벨:, 태그: 등 한국어/영어 키를 모두 지원한다.

body 정리 (clean_body): - H1 제거: Blogger가 게시글 제목을 별도로 렌더링하므로 마크다운 H1이 중복된다. clean_body에서 H1을 제거해 이중 표시를 방지한다. - 핵심 요약을 "핵심 요약"으로 치환 - [그림 : 설명] 형식을 이미지 플레이스홀더 div로 변환

HTML 변환: Python markdown 라이브러리로 마크다운을 HTML로 변환한다. extra, tables, fenced_code 확장을 사용한다.

CSS 주입 (BLOG_CSS): 모든 포스트에 동일한 스타일을 주입한다:

요소 스타일
본문 line-height 1.9, 16px
코드블록 다크 터미널 스타일 (배경 #1e1e2e)
테이블 헤더 파란색 배경 (#4a90d9)
blockquote 왼쪽 파란 테두리 + 밝은 배경, 부제용
구분선 2px solid #e0e0e0

CSS를 직접 주입하지 않으면 Blogger의 기본 테마 스타일이 코드블록과 테이블에 그대로 적용되어 가독성이 낮아진다.

3. 명령행 인터페이스

python3 blogger_publish.py draft.md

python3 blogger_publish.py draft.md --draft

python3 blogger_publish.py draft.md --update POST_ID

python3 blogger_publish.py --list

python3 blogger_publish.py --delete POST_ID

python3 blogger_publish.py draft.md --blog en

--blog kr/en 플래그로 한국어 블로그와 영문 블로그를 전환한다. 두 블로그는 별도 Blog ID로 관리되고, 인증은 하나의 token.json을 공유한다.

4. 목차 자동 업데이트 (update_toc.py)

글이 게시될 때마다 블로그의 목차 페이지를 자동 업데이트한다.

동작 방식: 1. Blogger API로 전체 게시글 목록 조회 2. 라벨 기반으로 카테고리 분류 (우선순위: 임베디드 > Claude Code > 로컬 LLM > OpenClaw > AI 에이전트) 3. HTML 목차 생성 (카테고리별 + 시리즈별 + 서브그룹별) 4. Blogger Pages API로 목차 페이지 업데이트

시리즈 글은 별도로 묶이고, OpenClaw 글은 "시작하기 / 설치 & 설정 가이드 / 아키텍처 & 심화" 서브그룹으로 분류된다.

blogger_publish.py가 게시 완료 후 자동으로 update_toc.py를 호출한다. --no-toc 플래그로 이 동작을 비활성화할 수 있다.

5. 대량 게시 — 쿼터 관리 설계

기존 글을 일괄 마이그레이션하거나 배치 발행할 때는 batch_publish.py를 사용한다.

for filepath in sorted_files:
    title, labels, html = parse_blog_post(filepath)
    publish_post(title, html, labels)
    time.sleep(10)  # 게시 간 10초 딜레이

쿼터 설계 포인트 3가지:

① 게시 간 10초 딜레이: Blogger API의 rate limit을 피하기 위한 기본 간격이다. 무료 쿼터 내에서 안정적으로 유지된다.

② 429 재시도 처리: Rate limit(HTTP 429)에 걸리면 60초 대기 후 재시도한다. 이 로직이 없으면 대량 게시 중 실패가 누적된다.

③ 배치 작업 시 TOC 호출 억제: 글 1건 게시 시 API 호출 구조는 다음과 같다: - 게시 1회 - 목차 업데이트: 전체 게시글 조회 1회 + 페이지 업데이트 1회

게시마다 목차를 업데이트하면 호출 수가 3배로 증가한다. 대량 작업 시에는 --no-toc 플래그로 목차 업데이트를 억제하고, 전체 게시 완료 후 한 번만 실행하는 것이 쿼터 효율적인 설계다.

기존 글 스타일 일괄 업데이트: CSS 주입을 파이프라인에 나중에 추가한 경우, 이미 게시된 글에 스타일이 없다. batch_update_style.py로 기존 포스트 전체에 CSS를 일괄 주입할 수 있다.

6. 한국어 + 영문 동시 운영

두 개의 블로그를 동시에 운영하는 구조:

블로그 용도 플래그
한국어 블로그 주 블로그, 한국어 콘텐츠 --blog kr (기본값)
영문 블로그 영문 콘텐츠, 동일 주제 --blog en

같은 주제를 한국어와 영문으로 각각 작성하고, 각 블로그에 발행한다. 스크립트 내부에서 Blog ID를 전환하므로 인증은 단일 token.json 하나만 유지한다.


마무리

Blogger API 자동 발행 파이프라인의 핵심 구조는 마크다운 → HTML → API 세 단계다. 복잡한 CMS 없이, 마크다운으로 글을 작성하고 스크립트 하나로 게시한다.

설계에서 가장 주의할 점은 쿼터 관리다. 무료 API는 접근성이 높지만 일일 호출 한도가 있다. 대량 작업 시에는 딜레이와 재시도 로직이 필수이고, 불필요한 연쇄 API 호출(매 게시마다 목차 업데이트 등)을 억제하는 것이 안정적인 운영의 핵심이다.

시리즈 전체 안내: 시리즈 목차

댓글