OpenAI 이미지 생성 API로 블로그 썸네일 자동화하기

OpenAI 공식 문서는 GPT Image 모델을 이용해 텍스트 프롬프트 기반 이미지 생성과 기존 이미지 편집을 지원한다고 설명합니다.

블로그 썸네일 자동화는 이미지 생성 요청 하나로 끝나지 않습니다. 파일 저장, 글자 검수, 워드프레스 미디어 등록, 대표 이미지 연결, 저장 결과 확인을 분리해야 실패했을 때 비용을 다시 쓰거나 엉뚱한 글의 이미지를 바꾸는 일을 줄일 수 있습니다.

아래 예제는 Python 3 환경에서 이미지 한 장을 만들고 기존 워드프레스 임시글에 대표 이미지로 연결하는 최소 구성입니다. 본문에 썸네일을 추가하거나 글을 공개하지 않습니다. 실제 유료 이미지 요청은 예제 검증을 위해 실행하지 않았습니다.

이 글에서 바로 찾기 5개 항목

생성과 업로드를 두 단계로 나눕니다

이미지 파일을 먼저 로컬에 보관하면 워드프레스 연결이 실패해도 그림부터 다시 만들 필요가 없습니다. 생성된 이미지의 한글·숫자·주제 일치와 작은 미리보기 가독성을 확인한 다음 업로드 단계를 실행하세요.

준비할 환경변수와 라이브러리

python -m pip install openai requests
  • OPENAI_API_KEY: 서버나 로컬 실행 환경에만 보관합니다.
  • OPENAI_IMAGE_MODEL: 자신의 프로젝트에서 사용 가능한 GPT Image 모델 ID를 지정합니다.
  • WP_BASE_URL: 자신의 워드프레스 HTTPS 주소입니다.
  • WP_USERNAME, WP_APP_PASSWORD: 미디어 업로드와 대상 글 수정 권한이 있는 계정 및 애플리케이션 비밀번호입니다.
  • WP_DRAFT_ID: 대표 이미지를 바꿀 기존 임시글의 숫자 ID입니다.

키를 소스·HTML·브라우저 JavaScript·Git에 넣지 마세요. 워드프레스는 일반 로그인 비밀번호 대신 애플리케이션 비밀번호 인증을 HTTPS에서 사용합니다.

광고

1단계: 이미지 한 장을 파일로 저장하기

다음 코드는 thumbnail.png가 이미 있으면 API를 호출하지 않습니다. 모델 접근 권한·지원 옵션·예산은 OpenAI 이미지 생성 문서와 자신의 프로젝트에서 확인하세요.

import base64
import os
from pathlib import Path
from openai import OpenAI

target = Path("thumbnail.png")
if target.exists():
    raise SystemExit("기존 thumbnail.png를 검수하세요. 다시 생성하지 않습니다.")

client = OpenAI(timeout=600.0, max_retries=0)
result = client.images.generate(
    model=os.environ["OPENAI_IMAGE_MODEL"],
    prompt=(
        "블로그 대표 이미지. 밝고 단순한 배경, 큰 한글 제목 "
        "'워드프레스 이미지 자동화', 보조 비주얼은 작은 이미지 파일 아이콘 하나. "
        "작은 미리보기에서도 제목이 읽히게 하고 표나 작은 설명문은 넣지 말 것."
    ),
)
if not result.data or not result.data[0].b64_json:
    raise RuntimeError("이미지 데이터가 없습니다. 응답 상태를 확인하세요.")

image_bytes = base64.b64decode(result.data[0].b64_json, validate=True)
with target.open("xb") as handle:
    handle.write(image_bytes)
print(target.resolve())

API 타임아웃은 결과가 전혀 생성되지 않았다는 보증이 아닙니다. 자동 재시도를 끈 예제이므로 오류가 나면 요청 기록과 사용량을 확인한 뒤 다음 호출을 결정합니다. 화면 크기·출력 형식·품질 옵션을 추가할 때는 선택한 모델의 지원값을 사용하세요.

광고

2단계: 임시글 확인 후 미디어와 대표 이미지 연결하기

아래 코드는 검수한 PNG를 업로드합니다. requests의 HTTP 오류를 확인하고, 대상이 임시글인지 검사한 뒤 featured_media만 수정합니다. 실행 중 이미지를 올린 다음 오류가 났다면 재실행 전에 미디어 라이브러리에서 중복 업로드 여부를 확인하세요.

import os
from pathlib import Path
from urllib.parse import urlparse
import requests

base = os.environ["WP_BASE_URL"].rstrip("/")
if urlparse(base).scheme != "https":
    raise ValueError("HTTPS 워드프레스 주소를 사용하세요.")
post_id = int(os.environ["WP_DRAFT_ID"])
api = base + "/wp-json/wp/v2"
session = requests.Session()
session.auth = (os.environ["WP_USERNAME"], os.environ["WP_APP_PASSWORD"])

def request(method, path, **kwargs):
    response = session.request(method, api + path, timeout=90, **kwargs)
    response.raise_for_status()
    return response.json()

post = request("GET", f"/posts/{post_id}?context=edit")
if post["status"] != "draft":
    raise RuntimeError("대상이 임시글이 아니므로 변경하지 않습니다.")

with Path("thumbnail.png").open("rb") as handle:
    media = request("POST", "/media", files={
        "file": ("thumbnail.png", handle, "image/png")
    })
print("업로드된 미디어 ID:", media["id"])
request("POST", f"/media/{media['id']}", json={
    "alt_text": "워드프레스 이미지 생성과 대표 이미지 연결 흐름",
})
request("POST", f"/posts/{post_id}", json={
    "featured_media": media["id"],
})
checked = request("GET", f"/posts/{post_id}?context=edit")
assert checked["status"] == "draft"
assert checked["featured_media"] == media["id"]
print("임시글 대표 이미지 연결 확인:", post_id)

미디어 등록은 WordPress Media REST API, 대표 이미지 연결은 Posts REST API의 필드입니다. 미디어 등록 성공과 글 연결 성공은 서로 다른 단계라 각각 확인해야 합니다.

워드프레스 이미지 업로드와 연결 흐름
워드프레스 이미지 업로드와 연결 흐름

한국어와 실패 처리는 자동화 밖으로 밀어내지 않습니다

AI가 만든 한글도 오탈자·줄바꿈·안전 여백을 검수해야 합니다. 정확한 표나 숫자가 중요하면 해당 부분을 HTML·CSS로 렌더링하는 방식이 유리할 수 있습니다. 이미지 생성 모델이 항상 글자에 실패한다거나 항상 정확하다고 가정하지 말고 실제 결과로 판단하세요.

실패한 단계다시 확인할 것
이미지 API인증, 모델 접근, 오류 코드, 호출 제한·결제 상태를 구분합니다. 429만 보고 잔액 0이라고 단정하지 않습니다.
미디어 업로드권한, 허용 형식, 용량, 보안 플러그인 응답을 확인합니다. 기존 로컬 파일을 재사용합니다.
대표 이미지 연결대상 ID·수정 권한·현재 상태와 업로드된 미디어 ID를 대조합니다.

장시간 운영하는 서비스라면 단계별 상태·미디어 ID를 저장하고 동시 수정·중복 요청에 대한 보호를 더해야 합니다. 실패 요청을 추적하는 방법은 실행 ID 중심 로그 설계와 연결해서 볼 수 있습니다.

썸네일 자동화의 완성 기준은 이미지가 만들어졌는지가 아닙니다. 검수한 파일이 정확한 글에 연결됐고, 실패한 단계를 불필요한 재생성 없이 이어갈 수 있어야 합니다.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다