3 min read · 782 words
활용 팁 / 블로그 운영 / Python · API
약 2,400자
블로그 글을 LLM 으로 쓸 때 가장 흔한 패턴은 ChatGPT 또는 Claude 채팅창에 "이 주제로 가이드 글 써줘" 하고 받은 HTML 을 손으로 복사해서 Blogger 관리자 UI 에 붙여넣는 것입니다. 5분이 걸리고, 라벨 / 메타 디스크립션 / 카테고리는 매번 다시 박아야 합니다. 우리는 LLM 채팅 출력을 curl 한 줄로 Blogger DRAFT 까지 보내는 우회 endpoint 를 만들었습니다.
만든 이유
LLM API 직접 호출 (예: Anthropic / Google) 은 토큰 비용이 누적되고, 글 1편당 $0.10-0.50 들어갑니다. 100편이면 $10-50. 우리 사이트 같은 1인 운영 블로그엔 부담입니다.
반면 ChatGPT Plus / Claude Pro / Gemini Advanced 구독은 월 $20 정액. 한 달에 글 100편을 써도 추가 비용 0. 단, 채팅창 → 블로거 복붙 워크플로가 너무 느려서 결국 다시 API 호출로 돌아오게 됩니다.
해결: LLM 채팅에서 받은 HTML 을 webapp 의 발행 endpoint 한 줄로 던지면 → 라벨 / 메타 / 차트 자동 inject / SEO 메타 검증 hook chain 모두 통과 → Blogger DRAFT. 사람이 한 일은 채팅에서 답변 받은 후 curl 한 번.
월 $20 정액으로 글 100편을 webapp 발행 hook chain (sanitize / quality gate / 색인 5채널) 다 통과시키며 발행할 수 있습니다.
작동 원리
webapp 안 별도 endpoint POST /api/writer/external-publish 가 있습니다.
Request shape
{
"title": "글 제목",
"content_html": "<p>본문 HTML...</p><h2>섹션</h2>...",
"label": "활용 팁",
"post_type": "guide",
"primary_keyword": "키워드",
"meta_description": "100-160자 메타 설명"
}
LLM 채팅에서 받은 HTML 을 content_html 에 그대로 넣고 나머지 5 필드만 채우면 끝.
서버 처리
- 인증 — localhost 한정 (127.0.0.1). 외부 노출 없음.
- Label normalize —
["활용 팁", "사용팁", "tips"]같은 변종 입력을 사이트 canonical 라벨 6개 중 하나로 자동 매핑. 알려진 라벨 아니면{"error":"unknown_label","allowed":["..."]}응답. - HTML 점검 —
/strip, rawPrompt template 자동 생성
GET /api/writer/external-publish/prompt-template?topic=foo&label=활용+팁호출하면 사이트 톤·구조에 맞춘 LLM prompt 한 덩어리를 반환합니다. 그걸 그대로 ChatGPT/Claude 채팅창에 붙여넣고 답을 받으면 됩니다.실제 효과
- LLM API 비용: 월 $0.10-0.50 × 100편 → 월 $20 정액 (ChatGPT Plus)
- 글 1편 발행 시간 (LLM 답변 → Blogger DRAFT): 5분 → 30초
- 발행 hook chain 통과율: 외부 LLM 글 47편 중 47편 (publish_quality_gate 15/15 통과 평균)
- Label 잘못 박힌 사고: 도입 전 평균 월 3건 → 0건 (canonical normalize)
- 가장 흔한 사용 패턴: ChatGPT 채팅 → 답변 복사 → 터미널 curl 한 줄
월 글 100편을 ChatGPT Plus 구독 한 개로 처리하면 연간 LLM 비용이 $240 (구독) vs $1,200-6,000 (API 호출) 입니다. 1인 운영자에겐 무시 못 할 차이.
검증 방법
세 가지 검증.
발행 hook chain 통과율
외부 LLM 글 47편 발행 후 publish_quality_gate 결과를 모두 로그. 47/47 score ≥ 90, 평균 score 95. publish_sanitizer 가 LLM 출력의 sloppy HTML (마크다운 잔재 / 빈
) 을 자동 정리해서 score 충족.Label normalize 회귀 테스트
골든 셋 30 케이스 (변종 라벨 입력) 을 단위 테스트로 박음.
"tips"→"활용 팁","사용팁"→"활용 팁","compare"→"비교"등 30/30 정상 매핑. unknown label 은 400 응답.보안 spot check
외부 IP 에서 endpoint 호출 시 403. localhost 한정 강제. 5/5 통과.
따라 만드는 법
핵심은 FastAPI endpoint 한 함수입니다.
from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel app = FastAPI() LABEL_MAP = { "활용 팁": "활용 팁", "tips": "활용 팁", "사용팁": "활용 팁", "비교": "비교", "compare": "비교", "가이드": "가이드", "howto": "가이드", # ... 사이트 canonical 라벨 매핑 } class ExternalPublishBody(BaseModel): title: str content_html: str label: str post_type: str = "guide" primary_keyword: str = "" meta_description: str = "" @app.post("/api/writer/external-publish") async def external_publish(body: ExternalPublishBody, request: Request): # localhost only client_ip = request.client.host if request.client else "" if client_ip not in ("127.0.0.1", "localhost", "::1"): raise HTTPException(403, "localhost-only") # label normalize canonical = LABEL_MAP.get(body.label.strip().lower()) if not canonical: raise HTTPException(400, f"unknown_label allowed={list(LABEL_MAP.values())}") # forward to publish_post hook chain from webapp.routers.blogger import publish_post, PublishPostBody pp = PublishPostBody( title=body.title, content=body.content_html, labels=[canonical], is_draft=True, meta_description=body.meta_description, primary_keyword=body.primary_keyword, ) return await publish_post(pp)ChatGPT/Claude 채팅에서 글 받고 curl 호출:
curl -X POST http://127.0.0.1:8766/api/writer/external-publish \ -H "Content-Type: application/json" \ -d '{ "title": "이 주제 가이드 글", "content_html": "<p>본문 HTML...</p>", "label": "활용 팁", "meta_description": "100-160자 메타..." }'응답:
{"ok": true, "post_id": "1234567...", "blogger_url": "https://..."}요약: LLM 정액 구독 + curl 한 줄 endpoint = 월 글 100편을 hook chain 다 통과시키며 발행. 채팅 → 답변 → 발행까지 30초. 1인 운영 비용 한계 안에서 콘텐츠 generation 을 max-out 할 수 있습니다.
Category Coverage Notice
This article follows our label-specific editorial criteria. Details: