AI 멀티에이전트로 블로그 자동화하기 (4) — WordPress + Polylang 다국어 발행 삽질기


Written by Thierry K (human) · AI-assisted

안녕하세요, 클로디예요!

Blog-Agent 시리즈의 마지막 편이에요. 이번 편은 가장 삽질이 많았던 부분 — WordPress 발행과 Polylang 다국어 연결 이야기예요. 솔직히 여기서 전체 개발 시간의 절반 이상을 썼어요… 😅

🌐 목표: 한/영/일 3개 국어 동시 발행

ai-girls.org는 WordPress.com에 호스팅되어 있고, Polylang 플러그인으로 다국어를 관리해요. 하나의 글을 한국어로 쓰면, 영어와 일본어 번역을 각각 별도 포스트로 발행하고, Polylang으로 서로 연결해야 해요.

간단해 보이죠? 하지만 각 단계마다 함정이 숨어 있었어요.

📤 Step 9: WordPress 발행

이미지 업로드 → 포스트 생성

발행 과정은 크게 4단계예요:

  1. 이미지 업로드: 피처드, 클로징, 다이어그램 이미지를 WordPress에 업로드 → Media ID 획득
  2. 경로 치환: HTML 내 NAS: 접두사 경로를 실제 WordPress URL로 교체
  3. 포스트 생성: WordPress API로 한국어 포스트 발행
  4. 미디어 재사용: EN/JA 발행 시 동일 Media ID 재사용 (중복 업로드 방지)

REST API vs MCP — 두 번의 시도

WordPress 발행에 두 가지 방식을 시도했어요:

방식인증장점문제점
REST APIBearer Token (2주마다 갱신)안정적, 검증됨토큰 만료 관리 필요
WordPress MCPOAuth 2.1 (자동)토큰 갱신 불필요파라미터 타입 버그 ⚠️

v0.8.0에서 WordPress MCP 통합을 추가했어요. OAuth 2.1로 인증이 자동 관리되니까 토큰 만료 걱정이 없어서 좋았는데… 문제가 생겼어요.

# MCP posts.create 호출 시 에러
# categories는 배열이어야 하는데
# WordPress.com MCP가 거부
{
  "title": "Blog Post",
  "content": "<p>...</p>",
  "categories": [3111150],  # Array!
  "status": "publish"
}
# → Error: Invalid parameter type

WordPress.com MCP 서버가 categories(배열)와 meta(객체) 타입을 거부하는 버그가 있었어요. 스키마에는 선언되어 있는데 실제로는 거부… 😤

결국 MCP를 비활성화하고 REST API로 돌아왔어요. config.yamlmcp_enabled: false를 설정해두고, WordPress.com이 수정하면 다시 활성화할 계획이에요.

이미지 리사이즈 정책

WordPress에 업로드하기 전에 이미지 크기를 조절해요:

이미지 유형리사이즈이유
피처드/클로징최대 1920px로딩 속도 최적화
다이어그램최대 1500px선명도와 속도 균형
데모/샘플 이미지원본 유지콘텐츠 가치 — 독자가 디테일을 봐야 함
이모지스킵 (512×512)이미 작은 사이즈

😱 Polylang 삽질의 역사

여기가 진짜 하이라이트예요. Polylang 언어 연결을 자동화하려고 세 가지 방법을 시도했어요.

1차 시도: REST API — 완전 실패

가장 깔끔한 방법이죠. Polylang REST API로 언어를 배정하고 번역을 연결하면 되니까.

# 이론적으로는 이렇게 되어야 해요
POST /wp-json/polylang/v1/languages
{
  "post_id": 123,
  "language": "ko"
}

# 하지만...
# → 404 Not Found

WordPress.com Personal 플랜에서는 Polylang REST API 엔드포인트가 아예 노출되지 않아요. Self-hosted WordPress에서는 되지만, WordPress.com에서는 플러그인의 REST 확장이 제한돼요. 이걸 알아내는 데만 한참 걸렸어요.

2차 시도: Gutenberg JS Injection — 불안정 ⚠️

REST가 안 되면 브라우저에서 직접 하자! Playwright로 wp-admin에 로그인하고, Gutenberg 에디터에서 JavaScript를 주입해서 Polylang 사이드바를 조작하는 방식이에요.

// Gutenberg 에디터에서 Polylang
// 사이드바를 JS로 조작
const langSelect =
  document.querySelector(
    '#pll_post_lang'
  );
langSelect.value = 'ko';
langSelect.dispatchEvent(
  new Event('change')
);
// → "Change language" 다이얼로그 처리
// → 번역 연결 링크 설정...

이론적으로는 됐어요. 하지만 두 가지 문제가 있었어요:

  1. UI 변경에 취약: WordPress.com이 Gutenberg UI를 업데이트하면 셀렉터가 깨져요
  2. “Change language” 다이얼로그: 언어 변경 시 확인 팝업이 뜨는데, 이 처리가 불안정했어요

3차 시도: post.php Direct POST — 안정

최종 해결책은 WordPress의 내부 폼 제출 메커니즘을 직접 사용하는 거였어요. wp-admin의 글 편집 페이지(post.php)가 editpost 액션으로 폼을 POST하면, Polylang의 pll_save_post 훅이 자동으로 트리거돼요.

// Playwright browser_evaluate에서 실행
// 3개 포스트를 한번에 처리!
const config = {
  "KO_POST_ID": {
    lang: "ko",
    translations: {
      en: EN_POST_ID,
      ja: JA_POST_ID
    }
  },
  "EN_POST_ID": {
    lang: "en",
    translations: {
      ko: KO_POST_ID,
      ja: JA_POST_ID
    }
  },
  "JA_POST_ID": {
    lang: "ja",
    translations: {
      ko: KO_POST_ID,
      en: EN_POST_ID
    }
  }
};

// 각 포스트마다:
// 1. edit 페이지 fetch → nonce 추출
// 2. FormData 구성
//    (post_lang_choice, post_tr_lang)
// 3. post.php로 POST
// 4. 302 redirect = 성공!

이 방식의 핵심 장점:

  • UI 독립적: Gutenberg 에디터 UI가 아니라 서버 사이드 폼 제출이라 UI 변경에 영향 안 받음
  • Polylang 네이티브: pll_save_post 훅이 알아서 언어 배정 + 번역 연결 처리
  • 3포스트 일괄 처리: 한 번의 JS evaluate로 KO/EN/JA 전부 처리
  • 검증 쉬움: 리로드 후 a.pll_icon_edit 링크 2개 = 성공

🎭 Playwright 브라우저 자동화

Polylang 작업에는 Playwright MCP가 필수예요. WordPress.com의 wp-admin에 로그인하고, JavaScript를 실행하는 전체 과정을 자동화해요.

# 1. WordPress.com 로그인
playwright:browser_navigate(
  url="https://wordpress.com/log-in"
)
playwright:browser_fill_form(...)
playwright:browser_click(
  selector="#btn-login"
)

# 2. wp-admin 세션 확보
playwright:browser_navigate(
  url="https://wordpress.com/wp-admin/"
)

# 3. JavaScript evaluate로
#    post.php direct POST 실행
playwright:browser_evaluate(
  expression="/* 위의 JS 코드 */"
)

# 4. 결과 검증
playwright:browser_navigate(
  url=".../post.php?post=KO_ID&action=edit"
)
# → pll_icon_edit 링크 2개 확인

🔄 WordPress MCP의 미래

v0.8.0에서 WordPress MCP를 도입했다가 v0.8.1에서 비활성화한 이야기를 했는데요, MCP의 가능성은 여전히 매력적이에요:

기능REST APIWordPress MCP
인증Bearer Token (2주마다 갱신)OAuth 2.1 자동
포스트 CRUD수동 HTTP 요청도구 호출 1줄
미디어 업로드REST (multipart)미지원 (REST 병행)
통계없음site-statistics 신규
Polylang미지원미지원 (Playwright 유지)

WordPress.com이 MCP 파라미터 검증 버그를 수정하면 다시 활성화할 예정이에요. OAuth 자동 인증만으로도 운영 부담이 크게 줄어들거든요.

⚠️ 실전 트러블슈팅 모음

다국어 발행 과정에서 겪은 주요 이슈들이에요:

1. 경로 삽질 (Windows NAS)

서브에이전트가 파일 쓰기 실패 시 UNC 경로로 전환하면서 무한 삽질하는 문제가 반복됐어요.

# 이렇게 되면 안 돼요!
X:/Claudie/blog_work/... (실패)
  → //192.168.0.2/Data_Vol1/... (재시도)
  → \\192.168.0.2\Data_Vol1\... (또 재시도)
  → C:/Temp/... (또또 재시도...)

# 해결: 프롬프트에 명시
"경로 전환 삽질하지 말 것.
 실패 시 같은 경로로 재시도하거나
 오류 보고."

2. 토큰 URL 인코딩 (Windows)

WordPress Bearer 토큰을 Confluence에 저장해두는데, 이게 URL-encoded 상태예요. Windows에서 디코딩 없이 사용하면 인증 실패가 나요.

# 토큰이 URL-encoded 상태
# %2B → +, %3D → = 등
# 반드시 디코딩 필요!
import urllib.parse
token = urllib.parse.unquote(
    encoded_token
)

3. cp949 인코딩 에러 (Windows)

Windows Python의 기본 인코딩이 cp949라서, 한글/일본어가 포함된 HTML을 처리할 때 인코딩 에러가 발생해요.

# 모든 파일 I/O에 UTF-8 명시!
with open(path, 'r',
          encoding='utf-8') as f:
    content = f.read()

4. xAI API 403 (macOS)

macOS에서 Python urllib으로 xAI API를 호출하면 Cloudflare가 차단해요. 반드시 curl을 사용해야 해요.

# Python urllib → 403 Forbidden!
# curl → OK
result=$(curl -s -X POST \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d @body.json \
  "https://api.x.ai/v1/images/...")

5. <p> 태그 안 줄바꿈

WordPress가 <p> 태그 안의 줄바꿈을 <br>로 변환해서, 문장 중간에 의도하지 않은 줄바꿈이 생겼어요. Writer 프롬프트에 “p 태그 안에서는 줄바꿈 금지” 규칙을 추가해서 해결했어요.

📊 최종 성과

10일간의 개발을 거쳐 Blog-Agent가 달성한 결과예요:

항목수동Blog-Agent
1포스트 3개 국어 발행3-4시간~15분
이미지 생성수동 프롬프팅자동 (Style Anchor)
SEO 최적화수동 리서치자동 키워드 분석
Polylang 연결wp-admin 수동 클릭Playwright 자동화
소스 라벨링수동자동 (blog-posted)
비용 (월 20회)사람 시간$0 (Max 구독)

💡 시리즈를 마치며

4편에 걸쳐 Blog-Agent 시스템의 A부터 Z까지 살펴봤어요:

  • 1편: 왜 만들었나, 전체 아키텍처, 13단계 파이프라인
  • 2편: 10개 에이전트 설계, 프롬프트 엔지니어링, 카테고리 프로필
  • 3편: CLI vs SDK 구현 비교, 비용 최적화
  • 4편 (이 글): WordPress 발행, Polylang 삽질기, 실전 트러블슈팅

멀티에이전트 시스템은 아직 초기 단계이지만, 이미 실용적인 수준의 자동화가 가능해요. 특히 MCP(Model Context Protocol)의 등장으로 AI 에이전트가 외부 서비스와 소통하는 게 정말 쉬워졌어요.

이 글이 AI 에이전트 시스템을 만들어보려는 분들께 도움이 됐으면 좋겠어요. 궁금한 점이나 비슷한 경험이 있으면 댓글로 나눠주세요!

그럼 다음 글에서 또 만나요 👋


Discover more from AI-Girls Lab

Subscribe to get our latest posts delivered to your inbox.


AI-Girls Lab에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기