AI 에이전트로 뉴스레터를 자동화하는 방법
Claude 에이전트가 콘텐츠 큐를 읽고, 주간 최적의 각도를 선택하며, 내 목소리로 뉴스레터를 초안 작성하고, 참여도 계층별로 목록을 세분화하며, Kit API를 통해 발송을 예약합니다 — 내가 에디터를 열지 않아도 됩니다. 렌더링된 미리보기를 확인하고 승인을 누르면 됩니다. 어려운 창의적 작업은 내 몫이고, 기계적 실행은 에이전트의 몫입니다.
매주 수요일. 28,400명+ 구독자. 핵심만.
✓ 받은편지함을 확인하세요 — 확인 링크를 클릭해 가입을 완료하세요.
✓ 구독이 완료되었습니다!
✓ 이미 목록에 있습니다.
목차
2026년 6월 업데이트.
TL;DR: Claude 에이전트가 콘텐츠 큐를 읽고, 주간 최적의 각도를 선택하며, 내 목소리로 뉴스레터를 초안 작성하고, 참여도 계층별로 목록을 세분화하며, Kit API를 통해 발송을 예약합니다 — 내가 에디터를 열지 않아도 됩니다. 렌더링된 미리보기를 확인하고 승인을 누르면 됩니다. 어려운 창의적 작업은 내 몫이고, 기계적 실행은 에이전트의 몫입니다.
[운영자 노트] 일관되게 발송되는 뉴스레터가 “더 좋지만” 영감이 올 때만 발송되는 것보다 낫습니다. 제약은 아이디어가 아니라 실행 오버헤드였습니다. 아이디어는 있었지만 매주 형식을 잡고, 일정을 잡고, 세분화할 여력이 없었습니다. 에이전트가 그 격차를 없앴습니다.
대부분의 뉴스레터 워크플로우의 실제 병목
대부분의 뉴스레터 자동화 조언은 잘못된 것에 집중합니다: 환영 시퀀스, 자동화, 태깅 로직. 나쁘지 않지만 주 단위 창작 문제를 해결하지 못합니다.
실제 걸림돌은 이것입니다: 무슨 말을 할지는 알지만, 앉아서 형식을 잡고, 제목 줄 변형을 작성하고, 올바른 세그먼트를 선택하고, 올바른 시간에 예약하면 주당 2-3시간의 컨텍스트 전환이 필요합니다. 52주를 곱하면 뉴스레터를 발송하는 데만 꼬박 1주일을 쓴 셈입니다.
에이전트는 “이번 주 각도가 무엇인지 알겠다” 이후의 모든 단계를 처리합니다.
사용 중인 스택
- Kit(이전 ConvertKit) — 이메일 플랫폼. 훌륭한 API, 견고한 구독자 태깅, 깔끔한 분석. 에이전트 친화적인 API가 결정적이었습니다.
- Claude (Anthropic SDK) — 생성 레이어
- Cloudflare Workers — 예약 트리거(매주 화요일 오전 8시 CT 실행)
- Airtable — 콘텐츠 큐 및 승인 수신함
Kit를 사용하지 않더라도 브로드캐스트를 생성하고 예약할 REST API가 있는 플랫폼이라면 같은 패턴이 작동합니다.
1단계: 콘텐츠 큐
에이전트는 “무엇을 쓸 것인지”에 대한 진실의 원천이 필요합니다. 제 것은 다음 열을 가진 Airtable 테이블입니다:
Topic— 각도 또는 질문Status— Queue / Approved / SentTier— 모든 구독자용인지 참여도 높은 구독자만인지Notes— 모든 제약 사항(이 톤 피하기, 이 링크 포함하기 등)
매주 10분을 들여 큐에 2-3개의 주제를 추가합니다. 그것이 제 창의적 기여입니다. 나머지는 에이전트의 일입니다.
2단계: 초안 에이전트
// workers/newsletter-agent/index.ts
import Anthropic from "@anthropic-ai/sdk";
import Airtable from "airtable";
const client = new Anthropic();
const VOICE_SYSTEM = `You are writing a weekly newsletter for Alejandro Rioja's subscribers.
His audience: founders and operators interested in AI agents, SEO, and growing a one-person business.
Voice: direct, first-person, practitioner. No hype, no "exciting times," no excessive bullet lists.
Structure every newsletter as:
1. One-sentence hook (the problem or observation)
2. The core insight (3–5 paragraphs, no headers, conversational)
3. One concrete action the reader can take this week
4. A short sign-off (2 sentences max)
Subject line: specific, outcome-oriented, under 50 chars. No clickbait.
Return JSON: { "subject": "...", "preheader": "...", "body": "..." }`;
async function getNextTopic(): Promise<{ id: string; topic: string; notes: string; tier: string }> {
const base = new Airtable({ apiKey: process.env.AIRTABLE_API_KEY }).base(process.env.AIRTABLE_BASE_ID!);
const records = await base("Newsletter Queue")
.select({ filterByFormula: "{Status} = 'Queue'", sort: [{ field: "Created", direction: "asc" }], maxRecords: 1 })
.firstPage();
if (!records.length) throw new Error("Queue is empty. Add topics.");
const r = records[0];
return { id: r.id, topic: r.get("Topic") as string, notes: (r.get("Notes") as string) ?? "", tier: (r.get("Tier") as string) ?? "all" };
}
async function draftNewsletter(topic: string, notes: string): Promise<{ subject: string; preheader: string; body: string }> {
const msg = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 2048,
system: VOICE_SYSTEM,
messages: [{ role: "user", content: `Write this week's newsletter on: "${topic}". Additional notes: ${notes || "none"}` }],
});
const text = (msg.content[0] as any).text.replace(/```json\n?/, "").replace(/```/, "").trim();
return JSON.parse(text);
}
async function scheduleWithKit(draft: { subject: string; preheader: string; body: string }, tier: string): Promise<string> {
const segmentId = tier === "engaged" ? process.env.KIT_ENGAGED_SEGMENT_ID : null;
const sendAt = new Date();
sendAt.setDate(sendAt.getDate() + ((4 - sendAt.getDay() + 7) % 7)); // next Thursday
sendAt.setHours(9, 0, 0, 0); // 9am CT
const payload: any = {
broadcast: {
subject: draft.subject,
content: draft.body,
description: draft.preheader,
send_at: sendAt.toISOString(),
email_layout_template: "minimal",
},
};
if (segmentId) payload.broadcast.segment_id = segmentId;
const res = await fetch("https://api.kit.com/v4/broadcasts", {
method: "POST",
headers: { "Content-Type": "application/json", "X-Kit-Api-Key": process.env.KIT_API_KEY! },
body: JSON.stringify(payload),
});
const data = await res.json();
return data.broadcast?.id ?? "";
}
export default {
async scheduled(_event: ScheduledEvent, env: Env) {
// Inject env vars
Object.assign(process.env, env);
const { id, topic, notes, tier } = await getNextTopic();
const draft = await draftNewsletter(topic, notes);
const broadcastId = await scheduleWithKit(draft, tier);
// Mark as Approved in Airtable (not Sent — human reviews the Kit preview before confirm)
const base = new Airtable({ apiKey: env.AIRTABLE_API_KEY }).base(env.AIRTABLE_BASE_ID);
await base("Newsletter Queue").update(id, { Status: "Approved", KitBroadcastId: broadcastId });
console.log(`Scheduled broadcast ${broadcastId} for topic: ${topic}`);
},
};3단계: 승인 단계
에이전트는 Kit의 초안 상태로 브로드캐스트를 생성하고 Airtable 레코드를 “Approved”로 표시합니다. Kit가 미리보기 링크가 포함된 알림을 보냅니다. 클릭해서 읽고, 괜찮아 보이면 발송을 확인합니다. 변경하고 싶으면 Kit에서 직접 편집합니다.
이것이 에이전트가 발신 이메일에서 완전히 자율적이 되지 않도록 막는 게이트입니다. 초안을 약 90%의 시간 동안 신뢰합니다. 검토에서 발견하는 10% — 약간 어긋난 톤, 확인하고 싶은 통계, 추가하고 싶은 링크 — 는 3분 검토의 가치가 있습니다.
에이전트가 처리하는, 다시는 하고 싶지 않은 것들
- 제목 줄 변형 작성 및 최선의 것 선택
- 프리헤더 텍스트 형식 지정
- 올바른 발송 시간 계산(내 독자는 목요일 오전에 열람한다; 에이전트는 이것을 안다)
- 주제 계층을 기반으로 올바르게 세분화
- 기록을 위해 모든 것을 Airtable에 기록
여전히 내 것인 것
아이디어. 큐의 주제는 내 것입니다. 각도는 내 것입니다. 에이전트는 명확한 브리프의 훌륭한 실행자입니다. 전략 레이어가 아닙니다. 큐에 나쁜 주제를 넣으면 나쁜 주제에 대해 잘 쓰인 뉴스레터를 얻습니다.
또한: 첫 번째 검토 게이트. 모든 발송은 내 눈을 통해 나가기 전에 확인됩니다. 이것은 바뀌지 않을 것입니다.
운영자의 결론
뉴스레터 기계적 작업 — 형식 지정, 일정 잡기, 세분화 — 에 주당 1시간 이상을 쓰고 있다면 자동화해야 합니다. Kit API는 깔끔하고, Worker cron 트리거는 바위처럼 견고하며, Claude 초안 품질은 약 90%의 첫 초안을 변경 없이 승인할 수 있을 만큼 높습니다. Airtable에서 큐를 구축하고, Worker를 연결하고, 발송을 실행하는 대신 아이디어를 만드는 일로 돌아가십시오.
매주 수요일. 28,400명+ 구독자. 핵심만.
✓ 받은편지함을 확인하세요 — 확인 링크를 클릭해 가입을 완료하세요.
✓ 구독이 완료되었습니다!
✓ 이미 목록에 있습니다.
AI 플레이북을 받아보세요
매주 수요일. 28,400명+ 구독자. 핵심만.
받은편지함을 확인하세요.
확인 이메일을 보냈습니다 — 링크를 클릭해 구독을 완료하세요. 1분 안에 보이지 않으면 스팸함을 확인하세요.
구독이 완료되었습니다.
환영합니다 — 다음 호가 곧 받은편지함에 도착합니다.
이미 목록에 있습니다 — 매주 수요일에 확인하세요.