블로그 글을 코드로 자동 발행하려면 Blogger API와 OAuth 2.0 인증이 필요합니다. Blogger 자동 발행을 예로 액세스 토큰 발급부터 글 생성과 공개 전환까지 정리합니다.
이 글의 가정: Google 계정과 Blogger 블로그가 있고, Google Cloud에서 OAuth 클라이언트(client_id, client_secret)와 refresh token을 이미 발급받았다고 가정합니다. 발급 절차 자체는 Google 공식 인증 흐름을 따릅니다. 비밀값은 코드에 직접 쓰지 말고 환경변수로 관리합니다.
OAuth 2.0 refresh token 흐름
refresh token(장기 보관용 재발급 토큰)은 한 번 발급받아 두고, API를 호출할 때마다 짧은 수명의 access token(실제 요청에 쓰는 접근 토큰)으로 교환해서 사용합니다. 즉 “refresh token → access token → API 호출” 순서입니다.
액세스 토큰 발급
토큰 교환은 Google 토큰 엔드포인트에 grant_type=refresh_token으로 요청합니다. 아래는 비동기 HTTP 클라이언트 httpx를 사용한 예입니다.
import os, httpx
TOKEN_URL = "https://oauth2.googleapis.com/token"
API_BASE = "https://www.googleapis.com/blogger/v3"
async def get_access_token():
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(TOKEN_URL, data={
"client_id": os.environ["BLOGGER_CLIENT_ID"],
"client_secret": os.environ["BLOGGER_CLIENT_SECRET"],
"refresh_token": os.environ["BLOGGER_REFRESH_TOKEN"],
"grant_type": "refresh_token",
})
resp.raise_for_status()
return resp.json()["access_token"]
여기서는 호출할 때마다 토큰을 새로 받는 단순한 방식을 씁니다. 토큰을 캐싱해 재사용하는 최적화는 별도로 추가할 수 있습니다.
글 생성 (초안 또는 공개)
글 생성은 posts 엔드포인트에 제목과 본문 HTML을 실어 보냅니다. isDraft 파라미터로 초안으로 만들지, 바로 공개할지 정합니다.
async def create_post(blog_id, title, html, as_draft=True):
token = await get_access_token()
params = {"isDraft": "true"} if as_draft else {}
url = f"{API_BASE}/blogs/{blog_id}/posts/"
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(
url,
headers={"Authorization": f"Bearer {token}"},
json={"title": title, "content": html},
params=params,
)
resp.raise_for_status()
return resp.json()["id"]
초안을 공개로 전환
초안으로 만들어 둔 글은 별도의 publish 엔드포인트로 공개 상태로 바꿉니다.
async def publish_draft(blog_id, post_id):
token = await get_access_token()
url = f"{API_BASE}/blogs/{blog_id}/posts/{post_id}/publish"
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(url, headers={"Authorization": f"Bearer {token}"})
resp.raise_for_status()
return resp.json().get("url")
정리하면 현재 흐름은 글 생성(create) + 발행(publish)으로 구성됩니다. 즉 새 글을 올리고 공개 상태로 전환하는 데까지가 이 코드의 범위입니다.
재시도 처리
외부 API는 상태코드뿐 아니라 요청이 멱등한지를 함께 봐야 합니다. GET처럼 다시 보내도 같은 자원을 읽는 요청은 429나 일시적인 5xx에서 제한된 횟수로 재시도할 수 있습니다. 반면 posts.insert는 새 글을 만드는 POST이므로, 요청을 보낸 뒤 타임아웃이나 5xx가 났다고 곧바로 같은 요청을 다시 보내면 첫 요청이 이미 처리된 경우 중복 글이 생길 수 있습니다.
- 읽기 요청: 429·일시적 5xx에서 Retry-After 또는 지수 백오프를 적용합니다.
- 글 생성(posts.insert): 응답 결과가 불명확하면 자동 재시도를 멈추고 최근 글 목록이나 관리자 화면에서 생성 여부를 먼저 확인합니다. 성공 응답을 받았다면 반환된 post ID를 저장합니다.
- 발행(publish): 이미 알고 있는 post ID를 사용하되, 응답이 불명확하면 현재 게시 상태를 조회한 뒤 다시 호출할지 결정합니다.
- 400·403·404: 요청·권한·대상 오류를 먼저 수정하고 같은 요청을 반복하지 않습니다. 401은 토큰 갱신 뒤 제한적으로 다시 시도합니다.
자동화에서는 로컬 작업 ID와 Blogger post ID를 함께 기록해, 같은 작업을 다시 실행할 때 이미 만들어진 글을 재사용할 수 있게 두는 편이 안전합니다.
마무리
refresh token으로 access token을 교환한 뒤 글을 생성하고 공개하는 것이 Blogger 자동 발행의 핵심 흐름입니다. 위 예제에서 반복적으로 쓰인 비동기 HTTP 호출 패턴은 httpx AsyncClient로 외부 API 비동기 호출하기에서 더 자세히 다룹니다. 발행 서버의 기반이 되는 FastAPI와 PostgreSQL 연동하기, 그리고 전체 자동화 그림은 유튜브 쇼츠를 Python으로 자동 생성하는 방법을 함께 보면 좋습니다.
이 글이 도움이 됐다면 구독과 공유 부탁드립니다.