공개 문서
docs/archive/2026-03/newsletter-pipeline/newsletter-pipeline.plan.md
아래는 docs/archive/2026-03/newsletter-pipeline/newsletter-pipeline.plan.md 와 동일한 원문입니다. Markdown과 HTML 변환 결과를 각각 복사할 수 있습니다.
공개 문서 원문 (Markdown)
# [Plan] newsletter-pipeline — Slack + Buttondown 뉴스레터 자동화 파이프라인
**Feature**: newsletter-pipeline
**Date**: 2026-03-01
**Phase**: Plan
**Level**: Dynamic (Next.js + Slack + Buttondown API)
**프로젝트**: slack-automation2
---
## 1. 목적 (Objective)
구독자 등록부터 뉴스레터 발송까지를 **하나의 자동화 파이프라인**으로 연결한다.
- 웹폼 또는 Slack 명령어로 구독자 등록
- 등록 즉시 Slack 알림 자동 전송
- Buttondown API로 구독자 관리 + 뉴스레터 발송
- 모든 활동 기록 저장
---
## 2. 전체 아키텍처 도식
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Pipeline A] 웹폼 경로
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┌─────────────┐ POST /api/subscribe ┌─────────────────┐
│ Web Form │ ─────────────────────────► │ Next.js API │
│ (이메일 입력) │ │ Route Handler │
└─────────────┘ └────────┬────────┘
│
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ Buttondown API │ │ Slack Webhook │ │ DB / 로컬 기록 │
│ 구독자 등록 │ │ 알림 자동 전송 │ │ 구독 이력 저장 │
│ POST /subscribers│ │ "새 구독자: email" │ │ subscribers.json│
└──────────────────┘ └─────────────────────┘ └──────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Pipeline B] Slack 슬래시 커맨드 경로
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Slack 채널
┌──────────────────────────┐
│ /subscribe user@mail.com │ ──► Slack → Next.js /api/slack/subscribe
└──────────────────────────┘ │
▼
(Pipeline A 와 동일)
Buttondown 등록 + Slack 알림 + 기록
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Pipeline C] 뉴스레터 발송 (Buttondown)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CEO 또는 자동 트리거
┌──────────────────┐ POST /api/newsletter/send ┌──────────────────┐
│ Slack 명령어 │ ──────────────────────────────► │ Next.js API │
│ /newsletter │ 또는 웹 대시보드 버튼 │ │
└──────────────────┘ └────────┬─────────┘
│
┌─────────────┴──────────────┐
▼ ▼
┌──────────────────┐ ┌─────────────────────┐
│ Buttondown API │ │ Slack 알림 │
│ 뉴스레터 발송 │ │ "발송 완료: N명" │
│ POST /emails │ └─────────────────────┘
└──────────────────┘
```
---
## 3. 핵심 컴포넌트
### 3.1 Next.js API Routes
| 엔드포인트 | 메서드 | 역할 |
|-----------|--------|------|
| `/api/subscribe` | POST | 웹폼 구독자 등록 |
| `/api/slack/subscribe` | POST | Slack 슬래시 커맨드 처리 |
| `/api/slack/events` | POST | Slack 이벤트 수신 |
| `/api/newsletter/send` | POST | 뉴스레터 발송 트리거 |
### 3.2 Buttondown API 연동
| 기능 | API | 설명 |
|------|-----|------|
| 구독자 등록 | `POST /v1/subscribers` | email 등록 |
| 구독자 조회 | `GET /v1/subscribers` | 전체 목록 |
| 뉴스레터 발송 | `POST /v1/emails` | 이메일 발송 |
| 구독 취소 | `DELETE /v1/subscribers/{id}` | 탈퇴 처리 |
### 3.3 Slack 연동
| 기능 | 방식 | 설명 |
|------|------|------|
| `/subscribe <email>` | Slash Command | 구독자 등록 |
| `/newsletter <제목> <내용>` | Slash Command | 뉴스레터 발송 |
| 신규 구독 알림 | Incoming Webhook | 자동 알림 |
| 발송 완료 알림 | Incoming Webhook | 발송 결과 |
### 3.4 기록 저장
| 데이터 | 저장 방식 | 위치 |
|--------|----------|------|
| 구독자 이력 | JSON 파일 | `data/subscribers.json` |
| 발송 이력 | JSON 파일 | `data/newsletters.json` |
| 활동 로그 | 텍스트 | `data/activity.log` |
---
## 4. 기술 스택
| 레이어 | 기술 |
|--------|------|
| Frontend | Next.js 14 (App Router) + Tailwind CSS |
| Backend | Next.js API Routes |
| 뉴스레터 | Buttondown API |
| Slack | Slack Bolt (slash commands) + Incoming Webhook |
| 저장소 | JSON 파일 (MVP) → Supabase (확장 시) |
| 배포 | Vercel |
---
## 5. 환경변수
```env
# Buttondown
BUTTONDOWN_API_KEY=your_api_key
# Slack
SLACK_BOT_TOKEN=xoxb-...
SLACK_SIGNING_SECRET=...
SLACK_WEBHOOK_URL=https://hooks.slack.com/...
# App
NEXT_PUBLIC_APP_URL=https://your-domain.vercel.app
```
---
## 6. 구현 우선순위 (MVP → 확장)
### MVP (즉시 구현)
1. `POST /api/subscribe` — 웹폼 → Buttondown 등록 + Slack 알림
2. 웹폼 UI (이메일 입력 + 제출)
3. Slack Incoming Webhook 알림
### Phase 2 (MVP 검증 후)
4. Slack `/subscribe` 슬래시 커맨드
5. 구독자 기록 저장 (JSON)
### Phase 3 (안정화 후)
6. Slack `/newsletter` 커맨드로 발송 트리거
7. 발송 이력 저장 + 통계
---
## 7. 폴더 구조
```
slack-automation2/
├── src/
│ └── app/
│ ├── page.tsx # 웹폼 페이지
│ ├── api/
│ │ ├── subscribe/
│ │ │ └── route.ts # 웹폼 구독 처리
│ │ ├── slack/
│ │ │ ├── subscribe/
│ │ │ │ └── route.ts # Slack /subscribe 처리
│ │ │ └── events/
│ │ │ └── route.ts # Slack 이벤트
│ │ └── newsletter/
│ │ └── send/
│ │ └── route.ts # 뉴스레터 발송
│ └── components/
│ └── SubscribeForm.tsx
├── lib/
│ ├── buttondown.ts # Buttondown API 클라이언트
│ ├── slack.ts # Slack 알림 유틸
│ └── storage.ts # 기록 저장 유틸
├── data/
│ ├── subscribers.json
│ └── newsletters.json
└── docs/
```
---
## 8. 핵심 플로우 (Buttondown API 코드 미리보기)
```typescript
// lib/buttondown.ts
const BUTTONDOWN_BASE = 'https://api.buttondown.email'
export async function addSubscriber(email: string) {
const res = await fetch(`${BUTTONDOWN_BASE}/v1/subscribers`, {
method: 'POST',
headers: {
'Authorization': `Token ${process.env.BUTTONDOWN_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, type: 'regular' }),
})
return res.json()
}
export async function sendNewsletter(subject: string, body: string) {
const res = await fetch(`${BUTTONDOWN_BASE}/v1/emails`, {
method: 'POST',
headers: {
'Authorization': `Token ${process.env.BUTTONDOWN_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ subject, body, status: 'about_to_send' }),
})
return res.json()
}
```
---
## 9. 리스크
| 리스크 | 대응 |
|--------|------|
| Buttondown API 응답 지연 | timeout 처리 + 사용자 피드백 |
| Slack 슬래시 커맨드 3초 응답 제한 | 즉시 ack → 백그라운드 처리 |
| 중복 구독 등록 | Buttondown 중복 거부 처리 |
| API 키 노출 | 환경변수만 사용, 클라이언트 미노출 |
---
## 10. 다음 액션
1. **지금**: `/pdca design newsletter-pipeline` — 상세 설계
2. **설계 후**: `/pdca do newsletter-pipeline` — Next.js 프로젝트 구현 시작
> **PDCA 다음 단계**: `/pdca design newsletter-pipeline`
공개 문서 변환 코드 (HTML)
<h1>[Plan] newsletter-pipeline — Slack + Buttondown 뉴스레터 자동화 파이프라인</h1>
<p><strong>Feature</strong>: newsletter-pipeline
<strong>Date</strong>: 2026-03-01
<strong>Phase</strong>: Plan
<strong>Level</strong>: Dynamic (Next.js + Slack + Buttondown API)
<strong>프로젝트</strong>: slack-automation2</p>
<hr>
<h2>1. 목적 (Objective)</h2>
<p>구독자 등록부터 뉴스레터 발송까지를 <strong>하나의 자동화 파이프라인</strong>으로 연결한다.</p>
<ul>
<li>웹폼 또는 Slack 명령어로 구독자 등록</li>
<li>등록 즉시 Slack 알림 자동 전송</li>
<li>Buttondown API로 구독자 관리 + 뉴스레터 발송</li>
<li>모든 활동 기록 저장</li>
</ul>
<hr>
<h2>2. 전체 아키텍처 도식</h2>
<pre><code>━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Pipeline A] 웹폼 경로
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┌─────────────┐ POST /api/subscribe ┌─────────────────┐
│ Web Form │ ─────────────────────────► │ Next.js API │
│ (이메일 입력) │ │ Route Handler │
└─────────────┘ └────────┬────────┘
│
┌────────────────────────┼────────────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ Buttondown API │ │ Slack Webhook │ │ DB / 로컬 기록 │
│ 구독자 등록 │ │ 알림 자동 전송 │ │ 구독 이력 저장 │
│ POST /subscribers│ │ "새 구독자: email" │ │ subscribers.json│
└──────────────────┘ └─────────────────────┘ └──────────────────┘
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Pipeline B] Slack 슬래시 커맨드 경로
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Slack 채널
┌──────────────────────────┐
│ /subscribe user@mail.com │ ──► Slack → Next.js /api/slack/subscribe
└──────────────────────────┘ │
▼
(Pipeline A 와 동일)
Buttondown 등록 + Slack 알림 + 기록
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Pipeline C] 뉴스레터 발송 (Buttondown)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CEO 또는 자동 트리거
┌──────────────────┐ POST /api/newsletter/send ┌──────────────────┐
│ Slack 명령어 │ ──────────────────────────────► │ Next.js API │
│ /newsletter │ 또는 웹 대시보드 버튼 │ │
└──────────────────┘ └────────┬─────────┘
│
┌─────────────┴──────────────┐
▼ ▼
┌──────────────────┐ ┌─────────────────────┐
│ Buttondown API │ │ Slack 알림 │
│ 뉴스레터 발송 │ │ "발송 완료: N명" │
│ POST /emails │ └─────────────────────┘
└──────────────────┘
</code></pre>
<hr>
<h2>3. 핵심 컴포넌트</h2>
<h3>3.1 Next.js API Routes</h3>
<table>
<thead>
<tr>
<th>엔드포인트</th>
<th>메서드</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>/api/subscribe</code></td>
<td>POST</td>
<td>웹폼 구독자 등록</td>
</tr>
<tr>
<td><code>/api/slack/subscribe</code></td>
<td>POST</td>
<td>Slack 슬래시 커맨드 처리</td>
</tr>
<tr>
<td><code>/api/slack/events</code></td>
<td>POST</td>
<td>Slack 이벤트 수신</td>
</tr>
<tr>
<td><code>/api/newsletter/send</code></td>
<td>POST</td>
<td>뉴스레터 발송 트리거</td>
</tr>
</tbody></table>
<h3>3.2 Buttondown API 연동</h3>
<table>
<thead>
<tr>
<th>기능</th>
<th>API</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>구독자 등록</td>
<td><code>POST /v1/subscribers</code></td>
<td>email 등록</td>
</tr>
<tr>
<td>구독자 조회</td>
<td><code>GET /v1/subscribers</code></td>
<td>전체 목록</td>
</tr>
<tr>
<td>뉴스레터 발송</td>
<td><code>POST /v1/emails</code></td>
<td>이메일 발송</td>
</tr>
<tr>
<td>구독 취소</td>
<td><code>DELETE /v1/subscribers/{id}</code></td>
<td>탈퇴 처리</td>
</tr>
</tbody></table>
<h3>3.3 Slack 연동</h3>
<table>
<thead>
<tr>
<th>기능</th>
<th>방식</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>/subscribe <email></code></td>
<td>Slash Command</td>
<td>구독자 등록</td>
</tr>
<tr>
<td><code>/newsletter <제목> <내용></code></td>
<td>Slash Command</td>
<td>뉴스레터 발송</td>
</tr>
<tr>
<td>신규 구독 알림</td>
<td>Incoming Webhook</td>
<td>자동 알림</td>
</tr>
<tr>
<td>발송 완료 알림</td>
<td>Incoming Webhook</td>
<td>발송 결과</td>
</tr>
</tbody></table>
<h3>3.4 기록 저장</h3>
<table>
<thead>
<tr>
<th>데이터</th>
<th>저장 방식</th>
<th>위치</th>
</tr>
</thead>
<tbody><tr>
<td>구독자 이력</td>
<td>JSON 파일</td>
<td><code>data/subscribers.json</code></td>
</tr>
<tr>
<td>발송 이력</td>
<td>JSON 파일</td>
<td><code>data/newsletters.json</code></td>
</tr>
<tr>
<td>활동 로그</td>
<td>텍스트</td>
<td><code>data/activity.log</code></td>
</tr>
</tbody></table>
<hr>
<h2>4. 기술 스택</h2>
<table>
<thead>
<tr>
<th>레이어</th>
<th>기술</th>
</tr>
</thead>
<tbody><tr>
<td>Frontend</td>
<td>Next.js 14 (App Router) + Tailwind CSS</td>
</tr>
<tr>
<td>Backend</td>
<td>Next.js API Routes</td>
</tr>
<tr>
<td>뉴스레터</td>
<td>Buttondown API</td>
</tr>
<tr>
<td>Slack</td>
<td>Slack Bolt (slash commands) + Incoming Webhook</td>
</tr>
<tr>
<td>저장소</td>
<td>JSON 파일 (MVP) → Supabase (확장 시)</td>
</tr>
<tr>
<td>배포</td>
<td>Vercel</td>
</tr>
</tbody></table>
<hr>
<h2>5. 환경변수</h2>
<pre><code class="language-env"># Buttondown
BUTTONDOWN_API_KEY=your_api_key
# Slack
SLACK_BOT_TOKEN=xoxb-...
SLACK_SIGNING_SECRET=...
SLACK_WEBHOOK_URL=https://hooks.slack.com/...
# App
NEXT_PUBLIC_APP_URL=https://your-domain.vercel.app
</code></pre>
<hr>
<h2>6. 구현 우선순위 (MVP → 확장)</h2>
<h3>MVP (즉시 구현)</h3>
<ol>
<li><code>POST /api/subscribe</code> — 웹폼 → Buttondown 등록 + Slack 알림</li>
<li>웹폼 UI (이메일 입력 + 제출)</li>
<li>Slack Incoming Webhook 알림</li>
</ol>
<h3>Phase 2 (MVP 검증 후)</h3>
<ol start="4">
<li>Slack <code>/subscribe</code> 슬래시 커맨드</li>
<li>구독자 기록 저장 (JSON)</li>
</ol>
<h3>Phase 3 (안정화 후)</h3>
<ol start="6">
<li>Slack <code>/newsletter</code> 커맨드로 발송 트리거</li>
<li>발송 이력 저장 + 통계</li>
</ol>
<hr>
<h2>7. 폴더 구조</h2>
<pre><code>slack-automation2/
├── src/
│ └── app/
│ ├── page.tsx # 웹폼 페이지
│ ├── api/
│ │ ├── subscribe/
│ │ │ └── route.ts # 웹폼 구독 처리
│ │ ├── slack/
│ │ │ ├── subscribe/
│ │ │ │ └── route.ts # Slack /subscribe 처리
│ │ │ └── events/
│ │ │ └── route.ts # Slack 이벤트
│ │ └── newsletter/
│ │ └── send/
│ │ └── route.ts # 뉴스레터 발송
│ └── components/
│ └── SubscribeForm.tsx
├── lib/
│ ├── buttondown.ts # Buttondown API 클라이언트
│ ├── slack.ts # Slack 알림 유틸
│ └── storage.ts # 기록 저장 유틸
├── data/
│ ├── subscribers.json
│ └── newsletters.json
└── docs/
</code></pre>
<hr>
<h2>8. 핵심 플로우 (Buttondown API 코드 미리보기)</h2>
<pre><code class="language-typescript">// lib/buttondown.ts
const BUTTONDOWN_BASE = 'https://api.buttondown.email'
export async function addSubscriber(email: string) {
const res = await fetch(`${BUTTONDOWN_BASE}/v1/subscribers`, {
method: 'POST',
headers: {
'Authorization': `Token ${process.env.BUTTONDOWN_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, type: 'regular' }),
})
return res.json()
}
export async function sendNewsletter(subject: string, body: string) {
const res = await fetch(`${BUTTONDOWN_BASE}/v1/emails`, {
method: 'POST',
headers: {
'Authorization': `Token ${process.env.BUTTONDOWN_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ subject, body, status: 'about_to_send' }),
})
return res.json()
}
</code></pre>
<hr>
<h2>9. 리스크</h2>
<table>
<thead>
<tr>
<th>리스크</th>
<th>대응</th>
</tr>
</thead>
<tbody><tr>
<td>Buttondown API 응답 지연</td>
<td>timeout 처리 + 사용자 피드백</td>
</tr>
<tr>
<td>Slack 슬래시 커맨드 3초 응답 제한</td>
<td>즉시 ack → 백그라운드 처리</td>
</tr>
<tr>
<td>중복 구독 등록</td>
<td>Buttondown 중복 거부 처리</td>
</tr>
<tr>
<td>API 키 노출</td>
<td>환경변수만 사용, 클라이언트 미노출</td>
</tr>
</tbody></table>
<hr>
<h2>10. 다음 액션</h2>
<ol>
<li><strong>지금</strong>: <code>/pdca design newsletter-pipeline</code> — 상세 설계</li>
<li><strong>설계 후</strong>: <code>/pdca do newsletter-pipeline</code> — Next.js 프로젝트 구현 시작</li>
</ol>
<blockquote>
<p><strong>PDCA 다음 단계</strong>: <code>/pdca design newsletter-pipeline</code></p>
</blockquote>