n8n·Gemini로 자체 호스팅 뉴스레터 구축
출처: Dev.to
핵심 요점
- 맞춤형 워크플로우는 100% 데이터 소유권, 커스텀 스타일 제어, 플랫폼 수수료 제로를 제공해 플랫폼 락인보다 우수합니다.
- n8n 웹훅 CORS 설정은 클라이언트‑사이드 제출 시 필수이며, 프로덕션에서 출처를 제한하면 무단 크로스‑사이트 변조를 방지할 수 있습니다.
- 데이터베이스 기본값은 upsert 업데이트 시 작동하지 않음. 재구독 사용자는 워크플로우에서 검증 토큰을 명시적으로 재생성해야 보안 취약점을 막을 수 있습니다.
- Gemini에 직접 REST API 요청을 하면 n8n AI 모델 노드보다 세밀한 페이로드 제어가 가능해 JSON 출력 제약을 엄격히 적용할 수 있습니다.
- 견고한 HTML 템플릿은 깊은 정제가 필요합니다. LLM 출력 필드(제목, URL 등)를 정제하면 이메일 클라이언트에서 발생할 수 있는 XSS를 방지합니다.
- 호스팅 비용을 거의 없앨 수 있음. 소규모·중간 규모 리스트라면 라즈베리 파이 같은 홈 서버에 n8n을 자체 배포하고 클라우드 무료 티어와 결합해 월 SaaS 비용을 0원으로 만들 수 있습니다.
블로그를 시작했을 때, 독자와 지속적으로 소통하기 위해 뉴스레터가 필요하다는 걸 알았습니다. 하지만 기존 마케팅 스택을 보면 답답했습니다. Mailchimp나 Substack에 가입하고, 구독자 수가 늘어날수록 상승하는 구독료를 내며, 깔끔한 코드베이스에 무거운 추적 스크립트를 삽입하고, 독자를 내 Astro 사이트의 디자인 시스템을 깨뜨리는 형식화된 템플릿에 강제하고 싶지 않았습니다.
저는 맞춤형, 자체 호스팅, 보안인 솔루션을 원했습니다. 이미 연구 워크플로우의 일부를 자동화하고 있었기에, Astro, Supabase, n8n, Resend, Gemini이라는 제가 이미 운영 중인 도구들로 직접 뉴스레터 엔진을 만들기로 했습니다.
이 글에서는 제가 어떻게 구축했는지, 마주친 기술적 난관, 그리고 이를 안전하고 확장 가능하게 만든 엔지니어링 상세 내용을 다룹니다.
시스템 아키텍처: 두 개의 워크플로우
자동화된 뉴스레터는 자체 호스팅된 n8n 인스턴스에서 구독 엔진(실시간 옵트인 처리)과 주간 큐레이션 엔진(다이제스트 생성·발송)이라는 두 개의 분리된 워크플로우로 구성됩니다.
아래는 Astro 프론트엔드, Supabase 데이터베이스, 외부 API 간 데이터 흐름을 나타낸 다이어그램입니다.
1. 구독 및 검증 흐름 (더블 옵트인)
실시간으로 새로운 요청, 검증 클릭, 구독 해지를 처리하는 워크플로우입니다.
2. 주간 큐레이션 및 발송 흐름
매주 수요일에 트리거되어 주간 다이제스트를 생성·개인화·전송하는 스케줄 워크플로우입니다.
파트 1: 구독 흐름 해결 & CORS 장벽 넘기
구독 흐름은 겉보기에 간단합니다. 사용자가 홈페이지에 이메일을 입력하면 데이터베이스에 저장되고 검증 링크가 발송됩니다.
하지만 클라이언트‑사이드 폼과 자체 호스팅 백엔드를 분리해서 구현하면서 CORS 보안과 데이터베이스 일관성 문제에 바로 부딪혔습니다.
1. Supabase 데이터베이스 설정
Supabase에 subscribers 테이블을 간단히 만들었습니다. 여기서 가장 중요한 부분은 각 사용자를 위한 고유 검증 토큰을 자동 생성하도록 하는 것입니다.
create table subscribers (
id uuid default gen_random_uuid() primary key,
email text unique not null,
status text default 'pending', -- pending, confirmed, unsubscribed
token uuid default gen_random_uuid() not null,
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
confirmed_at timestamp with time zone,
unsubscribed_at timestamp with time zone
);
2. CORS 장벽에 부딪히다
Astro 홈페이지에서 구독 폼은 표준 JavaScript fetch POST 요청으로 이메일 주소를 n8n 웹훅 URL에 직접 전송합니다.
첫 시도에서 콘솔에 빨간색 오류가 가득했습니다. 요청이 교차 출처(peripheral-stack.com → n8n 서브도메인)였기 때문에 브