PostAll API로 콘텐츠 워크플로 자동화하기: 시작 가이드
출처: Dev.to
나는 콘텐츠 API를 만들려고 시작한 것이 아니다. 복사‑붙여넣기를 멈추기 위해 시작했다.
매주 같은 의식이 반복된다. 문서를 열고, 빈 페이지를 바라보며, 헤드라인을 쓰고, 다시 삭제하고, 다시 쓴다. 이를 모든 클라이언트, 모든 제품 페이지, 모든 이메일 드립 캠페인에 곱하면… 나는 창의적인 작업을 하고 있지 않았다. 창의적인 척을 하면서 조립 라인 작업을 하고 있었던 것이다.
PostAll은 그 일을 멈추기 위해 내가 만든 스크립트에서 시작되었다. 다른 개발자들이 사용해도 되냐고 물어보면서 그 스크립트가 API 형태로 바뀐 것이 바로 PostAll API다.
이 가이드는 PostAll API를 여러분의 워크플로에 통합하는 방법을 단계별로 안내한다 — 인증, 실제로 사용할 엔드포인트, Python과 Node.js 각각의 동작 코드, 그리고 작동하기 전 어디서 오류가 발생할 수 있는지까지. 끝까지 읽으면 포맷팅된 CMS‑준비 콘텐츠를 프로그래밍 방식으로 생성하는 파이프라인을 갖추게 된다.
만들게 될 것
콘텐츠 브리프(키워드, 톤, 목표 길이) 리스트를 받아서 게시 준비가 된 콘텐츠를 반환하는 스크립트 — 적절한 포맷, 메타데이터, 그리고 프로덕션 환경에서 마주칠 레이트 리밋에 대한 오류 처리까지 포함한다.
구조는 다음과 같다:
[ CSV 형태의 브리프 ] → [ PostAll API ] → [ 포맷된 콘텐츠 객체 ] → [ 여러분의 CMS / 데이터베이스 ]
전체 동작 코드는 각 섹션 끝에 제공한다. 흥미로운 부분은 인라인으로 설명한다.
필수 조건
- API 사용이 활성화된 PostAll 계정 (무료 티어로도 충분 – 아래에 레이트 리밋 명시)
- Node.js 18+ 또는 Python 3.10+
- 두 언어 중 하나에서
async/await에 대한 기본 이해 - HTTP 클라이언트: Node에서는
axios혹은 기본fetch, Python에서는httpx
1단계: 인증
PostAll은 API 키 인증을 사용한다. 모든 요청은 Authorization 헤더에 키를 포함해야 한다.
키 발급 방법: 대시보드 → Settings → API Keys → Generate New Key
환경 변수에 저장하고 절대 코드에 하드코딩하지 말 것.
export PostAll_API_KEY="postall_live_xxxxxxxxxxxxxxxxxxxx"
키에는 두 가지 접두사가 있다: 프로덕션용 postall_live_, 샌드박스용 postall_test_. 샌드박스는 실제 형태의 응답을 플레이스홀더 콘텐츠와 함께 반환하므로, 요청 할당량을 소모하지 않고 파이프라인을 테스트할 때 유용하다.
다른 작업을 시작하기 전에 인증을 검증하라:
// verify-auth.js
const PostAll_API_BASE = "https://api.usepostall.io/v1";
async function verifyAuth() {
const response = await fetch(`${PostAll_API_BASE}/account`, {
headers: {
Authorization: `Bearer ${process.env.PostAll_API_KEY}`,
"Content-Type": "application/json",
},
});
if (!response.ok) {
// 401 = 키 오류, 403 = 키는 있지만 플랜에 API 사용 권한이 없음
throw new Error(`Auth failed: ${response.status} ${response.statusText}`);
}
const account = await response.json();
console.log(`Authenticated as: ${account.email}`);
console.log(`Plan: ${account.plan} | Requests remaining: ${account.quota.remaining}`);
return account;
}
verifyAuth().catch(console.error);
먼저 이 코드를 실행하라. 403이 반환되면 현재 플랜에 API 사용 권한이 없는 것이므로, 업그레이드하거나 지원팀에 문의한 뒤 디버깅을 진행하라.
2단계: 실제로 사용할 세 개의 엔드포인트
PostAll은 전체 REST API를 제공하지만, 자동화 시나리오의 90%는 다음 세 엔드포인트로 해결된다.
| 엔드포인트 | 메서드 | 동작 |
|---|---|---|
/v1/generate | POST | 단일 콘텐츠 생성 |
/v1/batch | POST | 여러 생성 작업을 큐에 넣기 (비동기) |
/v1/batch/:id | GET | 배치 작업 결과를 폴링 |
추가로 /v1/templates(GET)로 저장된 프롬프트 템플릿을 조회하고, /v1/content/:id(GET/PATCH/DELETE)로 기존 콘텐츠를 관리할 수 있지만, 먼저 generate와 batch부터 시작하라.
3단계: 첫 번째 생성 요청
파이프라인을 만들기 전에, 한 번의 콘텐츠를 처음부터 끝까지 생성해 보자.
요청 형태:
// generate-single.js
const PostAll_API_BASE = "https://api.usepostall.io/v1";
async function generateContent(brief) {
const response = await fetch(`${PostAll_API_BASE}/generate`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PostAll_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
type: brief.type, // "blog_post" | "product_description" | "email" | "social"
topic: brief.topic,
tone: brief.tone, // "professional" | "conversational" | "technical" | "playful"
target_length: brief.target_length, // 목표 단어 수, 절대적인 제한은 아님
format: "markdown", // "markdown" | "html" | "plain"
metadata: {
seo_optimize: true, // 응답에 meta_description 및 focus_keyword 추가
include_headings: true,
},
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Generation failed: ${error.message}`);
}
return response.json();
}
// 테스트
generateContent({
type: "blog_post",
topic: "how to reduce JavaScript bundle size in 2025",
tone: "technical",
target_length: 800,
}).then((result) => {
console.log("Title:", result.content.title);
console.log("Word count:", result.content.word_count);
console.log("Meta description:", result.metadata.meta_description);
// result.content.body 에는 전체 마크다운이 들어있음
});
응답 형태:
{
"id": "gen_01hx4k2m9f...",
"status": "complete",
"content": {
"title": "7 Ways to Reduce Your JavaScript Bundle Size in 2025",
"body": "## The bundle problem\n\nYour users...",
"word_count": 847,
"reading_time_minutes": 4
},
"metadata": {
"meta_description": "Learn the most effective strategies for reducing JavaScript bundle size...",