공개 문서

docs/work-log.md

아래는 docs/work-log.md 와 동일한 원문입니다. Markdown과 HTML 변환 결과를 각각 복사할 수 있습니다.

공개 문서 원문 (Markdown)

# slack-automation2 작업 내역 정리

**최종 갱신**: 2026-03-02  
**프로젝트**: Slack + Buttondown 뉴스레터 자동화 파이프라인

---

## 1. 프로젝트 개요

**목적**: 구독자 등록부터 뉴스레터 발송까지 하나의 자동화 파이프라인으로 연결

| 구분 | 내용 |
|------|------|
| 기술 스택 | Next.js 16 (App Router) + Tailwind CSS 4 |
| 외부 연동 | Buttondown API, Slack (Webhook + Slash Command) |
| 저장소 | JSON 파일 (MVP) → Supabase 확장 예정 |
| 배포 | Vercel |

---

## 2. PDCA 진행 현황

### Plan (완료)
- **문서**: `docs/01-plan/features/newsletter-pipeline.plan.md`
- 전체 아키텍처 정의 (Pipeline A/B/C)
- API 엔드포인트 설계
- MVP → Phase 2 → Phase 3 우선순위 정의

### Design (완료)
- **문서**: `docs/02-design/features/newsletter-pipeline.design.md`
- lib/buttondown, slack, storage 상세 설계
- API Route별 구현 스펙
- Slack App 설정 가이드

### Do (구현 완료)
- 아래 3절 참조

### Check (미진행)
- 테스트·검증 단계 대기

---

## 3. 구현 완료 항목

### 3.1 라이브러리 (lib/)

| 파일 | 역할 |
|------|------|
| `lib/buttondown.ts` | Buttondown API 클라이언트 (구독자 등록/조회, 뉴스레터 발송) |
| `lib/slack.ts` | Incoming Webhook 알림, Slash Command 서명 검증 (HMAC-SHA256) |
| `lib/storage.ts` | JSON 파일 기반 구독자·발송 이력 저장 |

### 3.2 API Routes

| 엔드포인트 | 메서드 | 구현 상태 | 역할 |
|-----------|--------|----------|------|
| `/api/subscribe` | POST | ✅ | 웹폼 구독 → Buttondown 등록 + Slack 알림 + 기록 |
| `/api/slack/subscribe` | POST | ✅ | Slack `/subscribe` 슬래시 커맨드 처리 |
| `/api/slack/subscribers` | POST | ✅ | Slack `/subscribers` — 구독자 수 조회 |
| `/api/slack/newsletter` | POST | ✅ | Slack `/newsletter` — 뉴스레터 발송 트리거 |
| `/api/newsletter/send` | POST | ✅ | 뉴스레터 발송 → Buttondown + Slack 알림 + 기록 |

### 3.3 프론트엔드

| 컴포넌트 | 역할 |
|----------|------|
| `app/page.tsx` | 메인 페이지 (뉴스레터 구독 안내) |
| `app/components/SubscribeForm.tsx` | 이메일 입력 폼, 상태(loading/success/error) 처리 |

### 3.4 환경 설정

| 항목 | 상태 |
|------|------|
| `.env.example` | ✅ 템플릿 제공 |
| `.env.local` | ✅ Buttondown API Key, Slack Webhook URL 설정됨 |

---

## 4. 미구현 / 보류 항목

| 항목 | Plan 문서 | 비고 |
|------|----------|------|
| `/api/slack/events` | 언급됨 | Slack 이벤트 수신용, MVP 범위 외 |
| `data/` 폴더 | - | 첫 구독/발송 시 자동 생성 |

---

## 5. 환경변수 현황

| 변수 | .env.local | 용도 |
|------|------------|------|
| `BUTTONDOWN_API_KEY` | ✅ 설정됨 | Buttondown API 인증 |
| `SLACK_WEBHOOK_URL` | ✅ 설정됨 | 구독/발송 알림 전송 |
| `SLACK_SIGNING_SECRET` | ⚠️ 비어 있음 | `/subscribe` Slash Command 검증에 필요 |
| `SLACK_BOT_TOKEN` | ⚠️ placeholder | 현재 미사용 |
| `NEXT_PUBLIC_APP_URL` | localhost:3000 | 배포 시 Vercel URL로 변경 |

---

## 6. 다음 액션 (권장)

1. **Slack Slash Command 설정** (api.slack.com/apps → Slash Commands)

   | Command | Request URL | Short Description | Usage Hint |
   |---------|-------------|-------------------|------------|
   | `/subscribe` | `https://slack-automation2.vercel.app/api/slack/subscribe` | 뉴스레터 구독 | `[이메일 주소]` |
   | `/subscribers` | `https://slack-automation2.vercel.app/api/slack/subscribers` | 구독자 수 조회 | (비워두기) |
   | `/newsletter` | `https://slack-automation2.vercel.app/api/slack/newsletter` | 뉴스레터 발송 | `[제목]` 또는 `[제목\|본문]` |

2. **로컬 테스트**
   - `npm run dev` → 웹폼 구독 동작 확인
   - Buttondown 대시보드에서 구독자 등록 확인
   - Slack 채널에 알림 수신 확인

3. **배포**
   - Vercel 배포 후 환경변수 설정
   - Slack Slash Command Request URL을 Vercel URL로 업데이트

4. **Check 단계**
   - 웹폼 / Slack `/subscribe` / 뉴스레터 발송 API 각각 검증
   - 중복 구독 에러 처리 확인

---

## 7. 폴더 구조 (현재)

```
slack-automation2/
├── src/
│   ├── app/
│   │   ├── page.tsx
│   │   ├── layout.tsx
│   │   ├── components/
│   │   │   └── SubscribeForm.tsx
│   │   └── api/
│   │       ├── subscribe/route.ts
│   │       ├── slack/subscribe/route.ts
│   │       ├── slack/subscribers/route.ts
│   │       ├── slack/newsletter/route.ts
│   │       └── newsletter/send/route.ts
│   └── lib/
│       ├── buttondown.ts
│       ├── slack.ts
│       └── storage.ts
├── docs/
│   ├── 01-plan/features/newsletter-pipeline.plan.md
│   ├── 02-design/features/newsletter-pipeline.design.md
│   └── work-log.md (본 문서)
├── .env.local
├── .env.example
└── package.json
```

공개 문서 변환 코드 (HTML)

<h1>slack-automation2 작업 내역 정리</h1>
<p><strong>최종 갱신</strong>: 2026-03-02<br><strong>프로젝트</strong>: Slack + Buttondown 뉴스레터 자동화 파이프라인</p>
<hr>
<h2>1. 프로젝트 개요</h2>
<p><strong>목적</strong>: 구독자 등록부터 뉴스레터 발송까지 하나의 자동화 파이프라인으로 연결</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td>기술 스택</td>
<td>Next.js 16 (App Router) + Tailwind CSS 4</td>
</tr>
<tr>
<td>외부 연동</td>
<td>Buttondown API, Slack (Webhook + Slash Command)</td>
</tr>
<tr>
<td>저장소</td>
<td>JSON 파일 (MVP) → Supabase 확장 예정</td>
</tr>
<tr>
<td>배포</td>
<td>Vercel</td>
</tr>
</tbody></table>
<hr>
<h2>2. PDCA 진행 현황</h2>
<h3>Plan (완료)</h3>
<ul>
<li><strong>문서</strong>: <code>docs/01-plan/features/newsletter-pipeline.plan.md</code></li>
<li>전체 아키텍처 정의 (Pipeline A/B/C)</li>
<li>API 엔드포인트 설계</li>
<li>MVP → Phase 2 → Phase 3 우선순위 정의</li>
</ul>
<h3>Design (완료)</h3>
<ul>
<li><strong>문서</strong>: <code>docs/02-design/features/newsletter-pipeline.design.md</code></li>
<li>lib/buttondown, slack, storage 상세 설계</li>
<li>API Route별 구현 스펙</li>
<li>Slack App 설정 가이드</li>
</ul>
<h3>Do (구현 완료)</h3>
<ul>
<li>아래 3절 참조</li>
</ul>
<h3>Check (미진행)</h3>
<ul>
<li>테스트·검증 단계 대기</li>
</ul>
<hr>
<h2>3. 구현 완료 항목</h2>
<h3>3.1 라이브러리 (lib/)</h3>
<table>
<thead>
<tr>
<th>파일</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>lib/buttondown.ts</code></td>
<td>Buttondown API 클라이언트 (구독자 등록/조회, 뉴스레터 발송)</td>
</tr>
<tr>
<td><code>lib/slack.ts</code></td>
<td>Incoming Webhook 알림, Slash Command 서명 검증 (HMAC-SHA256)</td>
</tr>
<tr>
<td><code>lib/storage.ts</code></td>
<td>JSON 파일 기반 구독자·발송 이력 저장</td>
</tr>
</tbody></table>
<h3>3.2 API Routes</h3>
<table>
<thead>
<tr>
<th>엔드포인트</th>
<th>메서드</th>
<th>구현 상태</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>/api/subscribe</code></td>
<td>POST</td>
<td>✅</td>
<td>웹폼 구독 → Buttondown 등록 + Slack 알림 + 기록</td>
</tr>
<tr>
<td><code>/api/slack/subscribe</code></td>
<td>POST</td>
<td>✅</td>
<td>Slack <code>/subscribe</code> 슬래시 커맨드 처리</td>
</tr>
<tr>
<td><code>/api/slack/subscribers</code></td>
<td>POST</td>
<td>✅</td>
<td>Slack <code>/subscribers</code> — 구독자 수 조회</td>
</tr>
<tr>
<td><code>/api/slack/newsletter</code></td>
<td>POST</td>
<td>✅</td>
<td>Slack <code>/newsletter</code> — 뉴스레터 발송 트리거</td>
</tr>
<tr>
<td><code>/api/newsletter/send</code></td>
<td>POST</td>
<td>✅</td>
<td>뉴스레터 발송 → Buttondown + Slack 알림 + 기록</td>
</tr>
</tbody></table>
<h3>3.3 프론트엔드</h3>
<table>
<thead>
<tr>
<th>컴포넌트</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>app/page.tsx</code></td>
<td>메인 페이지 (뉴스레터 구독 안내)</td>
</tr>
<tr>
<td><code>app/components/SubscribeForm.tsx</code></td>
<td>이메일 입력 폼, 상태(loading/success/error) 처리</td>
</tr>
</tbody></table>
<h3>3.4 환경 설정</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>상태</th>
</tr>
</thead>
<tbody><tr>
<td><code>.env.example</code></td>
<td>✅ 템플릿 제공</td>
</tr>
<tr>
<td><code>.env.local</code></td>
<td>✅ Buttondown API Key, Slack Webhook URL 설정됨</td>
</tr>
</tbody></table>
<hr>
<h2>4. 미구현 / 보류 항목</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>Plan 문서</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td><code>/api/slack/events</code></td>
<td>언급됨</td>
<td>Slack 이벤트 수신용, MVP 범위 외</td>
</tr>
<tr>
<td><code>data/</code> 폴더</td>
<td>-</td>
<td>첫 구독/발송 시 자동 생성</td>
</tr>
</tbody></table>
<hr>
<h2>5. 환경변수 현황</h2>
<table>
<thead>
<tr>
<th>변수</th>
<th>.env.local</th>
<th>용도</th>
</tr>
</thead>
<tbody><tr>
<td><code>BUTTONDOWN_API_KEY</code></td>
<td>✅ 설정됨</td>
<td>Buttondown API 인증</td>
</tr>
<tr>
<td><code>SLACK_WEBHOOK_URL</code></td>
<td>✅ 설정됨</td>
<td>구독/발송 알림 전송</td>
</tr>
<tr>
<td><code>SLACK_SIGNING_SECRET</code></td>
<td>⚠️ 비어 있음</td>
<td><code>/subscribe</code> Slash Command 검증에 필요</td>
</tr>
<tr>
<td><code>SLACK_BOT_TOKEN</code></td>
<td>⚠️ placeholder</td>
<td>현재 미사용</td>
</tr>
<tr>
<td><code>NEXT_PUBLIC_APP_URL</code></td>
<td>localhost:3000</td>
<td>배포 시 Vercel URL로 변경</td>
</tr>
</tbody></table>
<hr>
<h2>6. 다음 액션 (권장)</h2>
<ol>
<li><p><strong>Slack Slash Command 설정</strong> (api.slack.com/apps → Slash Commands)</p>
<table>
<thead>
<tr>
<th>Command</th>
<th>Request URL</th>
<th>Short Description</th>
<th>Usage Hint</th>
</tr>
</thead>
<tbody><tr>
<td><code>/subscribe</code></td>
<td><code>https://slack-automation2.vercel.app/api/slack/subscribe</code></td>
<td>뉴스레터 구독</td>
<td><code>[이메일 주소]</code></td>
</tr>
<tr>
<td><code>/subscribers</code></td>
<td><code>https://slack-automation2.vercel.app/api/slack/subscribers</code></td>
<td>구독자 수 조회</td>
<td>(비워두기)</td>
</tr>
<tr>
<td><code>/newsletter</code></td>
<td><code>https://slack-automation2.vercel.app/api/slack/newsletter</code></td>
<td>뉴스레터 발송</td>
<td><code>[제목]</code> 또는 <code>[제목|본문]</code></td>
</tr>
</tbody></table>
</li>
<li><p><strong>로컬 테스트</strong></p>
<ul>
<li><code>npm run dev</code> → 웹폼 구독 동작 확인</li>
<li>Buttondown 대시보드에서 구독자 등록 확인</li>
<li>Slack 채널에 알림 수신 확인</li>
</ul>
</li>
<li><p><strong>배포</strong></p>
<ul>
<li>Vercel 배포 후 환경변수 설정</li>
<li>Slack Slash Command Request URL을 Vercel URL로 업데이트</li>
</ul>
</li>
<li><p><strong>Check 단계</strong></p>
<ul>
<li>웹폼 / Slack <code>/subscribe</code> / 뉴스레터 발송 API 각각 검증</li>
<li>중복 구독 에러 처리 확인</li>
</ul>
</li>
</ol>
<hr>
<h2>7. 폴더 구조 (현재)</h2>
<pre><code>slack-automation2/
├── src/
│   ├── app/
│   │   ├── page.tsx
│   │   ├── layout.tsx
│   │   ├── components/
│   │   │   └── SubscribeForm.tsx
│   │   └── api/
│   │       ├── subscribe/route.ts
│   │       ├── slack/subscribe/route.ts
│   │       ├── slack/subscribers/route.ts
│   │       ├── slack/newsletter/route.ts
│   │       └── newsletter/send/route.ts
│   └── lib/
│       ├── buttondown.ts
│       ├── slack.ts
│       └── storage.ts
├── docs/
│   ├── 01-plan/features/newsletter-pipeline.plan.md
│   ├── 02-design/features/newsletter-pipeline.design.md
│   └── work-log.md (본 문서)
├── .env.local
├── .env.example
└── package.json
</code></pre>