Claude Code로 Blogger
자동 발행 시스템 만들기
HTML 파일 하나 넣으면 매일 오전 8시에 알아서 발행됩니다.
왜 만들었나
Blogger에 글을 올릴 때마다 같은 작업이 반복됐습니다. 파일 열기 → 내용 복사 → 제목·라벨·퍼머링크 입력 → 발행. AdSense SEO를 위해 하루 1개씩 꾸준히 올려야 하는데, 이걸 매일 손으로 하는 건 지속하기 어려웠습니다.
그래서 Claude Code로 Python + Blogger API v3 기반 자동화 시스템을 만들었습니다. HTML 파일을 blog/ 폴더에 넣어두기만 하면, 스크립트가 알아서 읽고 예약 발행합니다. 구현 시간은 1시간도 안 걸렸습니다.
전체 구조와 동작 흐름
3개의 Python 파일과 설정 파일들로 구성됩니다.
동작 흐름은 단순합니다.
HTML 메타 주석 포맷
각 HTML 파일 최상단에 주석을 달아둡니다. Blogger에 등록할 때 필요한 모든 정보가 여기에 있습니다. 파이썬 파서가 이 주석을 읽어 API에 넘깁니다.
<!-- === BLOGGER 포스팅 가이드 === 제목 : 실제 제목 [대카테고리 · 소주제 · N편] 퍼머링크 : slug-in-english 메타 설명 : 160자 이내 설명. 핵심 키워드 포함. 라벨(7개) : [대카테고리] Claude Code [소주제] Blogger자동화 [기술키워드] Python자동화, Blogger API, Google OAuth2 발행 후 : 구글 서치콘솔에서 URL 색인 생성 요청 필수 ================================ -->
파서 코드 (html_parser.py)
import re from dataclasses import dataclass from pathlib import Path @dataclass class PostMeta: title: str permalink: str description: str labels: list[str] content: str filename: str def parse_html_file(filepath: Path) -> PostMeta: text = filepath.read_text(encoding='utf-8') comment_match = re.search(r'<!--(.*?)-->', text, re.DOTALL) if not comment_match: raise ValueError(f"메타 주석을 찾을 수 없습니다: {filepath}") comment = comment_match.group(1) content = text[comment_match.end():].strip() # 주석 이후 전체가 Blogger 본문 return PostMeta( title=_field(comment, '제목'), permalink=_field(comment, '퍼머링크'), description=_parse_description(comment), labels=_parse_labels(comment), content=content, filename=filepath.name, )
Google OAuth2 인증 (blogger_auth.py)
처음 한 번만 브라우저 인증을 하면 token.json에 저장됩니다. 이후 실행부터는 토큰 만료 여부를 자동으로 확인하고 갱신합니다.
from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow SCOPES = ['https://www.googleapis.com/auth/blogger'] TOKEN_PATH = Path('token.json') def get_credentials() -> Credentials: creds_file = Path(os.getenv('GOOGLE_CREDENTIALS_FILE', 'credentials.json')) creds = None if TOKEN_PATH.exists(): creds = Credentials.from_authorized_user_file(str(TOKEN_PATH), SCOPES) if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) # 만료 시 자동 갱신 else: flow = InstalledAppFlow.from_client_secrets_file(str(creds_file), SCOPES) creds = flow.run_local_server(port=0) # 최초 1회 브라우저 인증 TOKEN_PATH.write_text(creds.to_json(), encoding='utf-8') return creds
Google 계정 인증 토큰이 담겨 있어 외부에 노출되면 안 됩니다.
스케줄러 핵심 코드 (post_scheduler.py)
미발행 파일 목록 추출
def all_post_files() -> list[Path]: return sorted(BLOG_DIR.glob('post*.html')) # 파일명 오름차순 정렬 def pending_files(log: dict) -> list[Path]: return [f for f in all_post_files() if f.name not in log]
post1.html, post10.html을 알파벳순 정렬하면 post10이 post2보다 앞에 옵니다.처음부터
post001.html, post002.html 형식을 써야 순서가 보장됩니다.
Blogger API로 포스팅
def create_post(service, meta: PostMeta, publish_time=None) -> dict: body = { 'title': meta.title, 'content': meta.content, # <style> + 본문 div 전체 'labels': meta.labels, } if publish_time: body['published'] = publish_time.isoformat() # KST → ISO 8601 return service.posts().insert( blogId=BLOG_ID, body=body, isDraft=False, fetchBody=False, ).execute()
Blogger API v3는 퍼머링크 직접 지정을 지원하지 않습니다. 발행 후 관리자 화면에서 수동으로 설정해야 합니다. 스크립트가 발행 후 안내 문구를 출력해줍니다.
핵심 기능: 예약 날짜 연속성 유지
처음에는 --schedule-all을 실행하면 항상 "지금 이후 첫 오전 8시"부터 날짜를 잡았습니다. 이 방식은 나중에 HTML 파일을 추가하고 다시 실행하면 기존 예약 날짜와 충돌하는 문제가 있었습니다.
4월 27일 예약 완료 후 새 파일 추가 →
--schedule-all 재실행 → 새 파일이 다시 4월 27일부터 잡힘 (이미 예약된 날짜와 겹침)
로그에서 가장 마지막 날짜를 찾아 그 다음 날 8시부터 배정합니다. 로그가 비어 있으면 다음 오전 8시 KST를 기준으로 합니다.
def next_base_date(log: dict) -> datetime: latest = None for entry in log.values(): date_str = entry.get('scheduled_at') or entry.get('posted_at') if not date_str: continue dt = datetime.fromisoformat(date_str).astimezone(KST) dt_8am = dt.replace(hour=8, minute=0, second=0, microsecond=0) if latest is None or dt_8am > latest: latest = dt_8am if latest is None: return next_8am_kst() candidate = latest + timedelta(days=1) return candidate if candidate > datetime.now(KST) else next_8am_kst()
이 함수 덕분에 나중에 HTML 파일을 추가해도 기존 예약 일정에 이어서 날짜가 자동 배정됩니다.
발행 이력: posted_log.json
한 번 발행된 파일은 posted_log.json에 기록됩니다. 스크립트는 이 파일을 보고 "이미 처리된 것"을 건너뜁니다.
"post001_기획부터완성까지.html": {
"post_id": "1234567890",
"url": "https://yourblog.blogspot.com/2026/04/...",
"permalink_target": "claude-code-blogger-auto-posting",
"posted_at": "2026-04-26T08:00:00+09:00"
},
"post002_두번째글.html": {
"post_id": "9876543210",
"url": "https://yourblog.blogspot.com/2026/04/...",
"permalink_target": "my-second-post",
"scheduled_at": "2026-04-27T08:00:00+09:00",
"posted_at": "2026-04-26T09:30:00+09:00"
}
}
posted_at은 API를 호출한 시간, scheduled_at은 Blogger에 예약된 발행 시간입니다. --schedule-all로 예약하면 두 필드가 모두 기록됩니다.
명령어 4가지
| 명령어 | 동작 | 언제 쓰나 |
|---|---|---|
python post_scheduler.py | 다음 미발행 글 1개 즉시 발행 | Task Scheduler 자동 실행 |
--schedule-all | 미발행 전체를 8시 예약 등록 | 한 번에 모두 예약할 때 |
--list | 전체 발행 현황 출력 | 진행 상태 확인 |
--dry-run | 실제 발행 없이 내용 미리보기 | 테스트 및 최초 인증 확인 |
실행 예시
$ python post_scheduler.py --schedule-all 미발행 3개를 8시 예약 포스팅합니다. [1/3] post003_이메일자동발송.html 제목 : Python으로 분석 결과 자동 이메일 발송하기 예약 : 2026-04-28 08:00 KST [2/3] post004_결과후기.html 제목 : 실제 PDF 리포트 공개 + Ollama 도전기 예약 : 2026-04-29 08:00 KST [완료] 3개 예약 포스팅 등록되었습니다. ── 수동 작업 필요 ────────────────────────────────── 1. Blogger 관리자 → 퍼머링크 → 사용자 설정 → slug 입력 2. 검색 설명 입력 (메타 디스크립션) 3. 구글 서치콘솔 URL 색인 생성 요청 ────────────────────────────────────────────────────
발행 후 반드시 해야 하는 수동 작업
Blogger API v3의 한계상 자동화할 수 없는 작업이 3가지 있습니다. 스크립트 실행 후 출력에 각 항목이 그대로 안내되니 복사해서 쓰면 됩니다.
실제 사용 결과 및 소감
✓ HTML 파일 추가만으로 자동 예약 발행
✓ 새 파일 추가 시 기존 예약 날짜에 이어서 자동 배정
✓ OAuth2 토큰 만료 자동 갱신 (무인 실행 가능)
✓ 발행 이력 JSON으로 기록 (중복 발행 방지)
✓ dry-run으로 실제 발행 전 내용 확인
✓ 발행 후 수동 작업 항목 자동 안내
Claude Code와 함께한 후기
처음에는 "직접 짜도 금방 되겠지"라고 생각했습니다. 그런데 OAuth2 플로우, API 할당량, 예약 날짜 로직 같은 부분에서 막히는 포인트가 생각보다 많았습니다.
Claude Code는 코드를 직접 쓰고, 실행하고, 오류가 나면 수정하는 과정을 사람처럼 처리했습니다. "새 파일 추가 시 기존 예약 날짜를 이어받아야 한다"는 요구사항도 한 번에 이해하고 next_base_date() 함수로 정리해줬습니다. 혼자 했다면 몇 배는 걸렸을 작업이었습니다.
지금은 HTML 파일을 blog/ 폴더에 넣고 --schedule-all 한 번 실행하면 끝입니다. 그 이후는 매일 오전 8시에 알아서 올라갑니다.
댓글 없음:
댓글 쓰기