Dev Lab2 min read
서버·DB 없이 만든 블로그 구독 시스템: 설계·구현·교훈의 기록
Next.js 내장 서버와 Gmail API로 구독을 구현하고 PinCode 인증을 붙였습니다. 운영 중 겪은 nonce 충돌과 다중 Relayer 결정, 그리고 “결국 서버가 필요하다”는 결론까지 한 번에 공유합니다.
#블로그#구독#서버리스#Next.js#Gmail#API#핀코드#인증
독자는 이 글을 통해 서버·DB 없이도 동작하는 구독 시스템의 최소 구현과 한계를 빠르게 파악하고, 유사 상황에서의 기술 선택을 더 신중하게 할 수 있습니다.

왜 지금 구독 시스템인가
- 콘텐츠 발행 주기와 독자 유입을 연결하려면 알림 채널이 필요합니다.
- 비용·운영 부담을 낮추기 위해 초기에 서버와 DB를 두지 않는 전략을 선택했습니다.
- 목적은 “작동하는 가장 작은 것(MVP)”을 빠르게 검증하는 것이었습니다.
아키텍처 제약과 선택
- 서버 없음: 자체 서버가 없어 Next.js의
Route Handler를 서버 엔드포인트로 사용 - DB 없음: 영속 저장소가 없어 구독 요청을 “메일 수신함”과 임시 파일/환경 변수로 대체
- 메일 비용 회피: 외부 메일 서비스 대신 Gmail API를 사용해 개인 메일로 발송
핵심 전제: 초기에는 “운영비 최소화와 단순성”이 “확장성과 안정성”보다 우선
구현 핵심: Next.js·Gmail·PinCode
Next.js Route Handler
/api/subscribe에서 이메일 수집 → 검증 → 인증 메일 발송/api/verify에서 PinCode 검증 후 구독 확정. 성공/실패는 간단한 JSON 응답
Gmail API로 발송
gmail.users.messages.send로 메일 전송- 스로틀과 일일 한도 내에서 메시지 큐 + 워커(비동기 전송) → 초기엔 충분했으나 피크 시 제약 발생
PinCode 인증 흐름
- 사용자가 이메일 입력
- 서버(=Next.js 라우트)가 6자리 PinCode 생성 후 메일로 전송
- 사용자가 PinCode 제출 → 일치 시 구독 확정
주의: 임시 상태를 파일/메모리에 두면 재배포·스케일 아웃 시 유실 위험이 큽니다.
트랜잭션과 nonce 충돌
- 구독 확정 시 온체인 기록 또는 Relayer 호출이 빠르게 연속 실행되며 nonce 충돌이 발생했습니다.
- 단일 Relayer에서 직렬화가 보장되지 않아, 다중 Relayer를 도입해 트래픽을 분산했습니다.
- 이 부분은 다음 게시물에서 상세히 작성됩니다.
충돌 원인 요약
- 빠른 TX 전송으로 동일 계정의 nonce가 경쟁 상태에 진입
- 재시도 로직 부재와 큐잉 미흡으로 중복/실패 증가
응급 처치
- Relayer 풀 구성으로 요청 분산
- 전송 간 최소 지연 삽입, 실패 시 백오프 적용
- 상태 불일치 최소화를 위해 “최종 확정 이벤트”만을 기준으로 마킹
설계 대안 비교 표
| 의사결정 | 선택안 A (현재) | 선택안 B | 비고 |
|---|---|---|---|
| 서버 | 없음(Next.js 라우트) | 경량 서버(예: Edge/서버리스 함수) | 배포/상태 관리 난이도 차이 |
| 메일 | Gmail API(개인) | 전문 서비스(SendGrid 등) | 한도/정책 vs 안정성/분석 |
| 상태 | 임시/무상태 | 관리형 KV/DB | 인증/구독 상태 일관성 |
| Relayer | 다중(분산) | 단일(직렬화) | 처리량 vs 단순성 |
팁: 초기에 비용을 아끼더라도, 인증/발송/상태는 각기 독립된 실패 도메인으로 분리해보세요.
배운 점과 다음 단계
- 배운 점 1: 서버·DB 없이도 MVP는 가능하지만, 인증/발송/상태 일관성에서 즉시 한계를 만난다.
- 배운 점 2: 메일 발송은 비용뿐 아니라 정책·한도·품질 관리가 핵심이다.
- 배운 점 3: 온체인/Relayer 통합은 속도보다 순서 보장과 재시도 전략이 중요하다.
만약 실제 서비스 시 권장 아키텍처
- 관리형 스토리지(KV/DB)로 PinCode·구독 상태를 영속화
- 큐(작업 대기열) + 워커로 발송/온체인 작업을 비동기화
- 아이들링 비용 최소의 서버리스 함수로 API를 분리
- 메일 서비스 도입(서명/평판/반송 분석) 또는 Gmail API의 백오프·쿼터 관리 강화
코드와 레포
- 참고 링크 참조
결론: 이번 시도는 “작동하는 최소한”을 입증했지만, 역시나 개인적인 생각으로는 안정적 운영을 위해 서버(또는 함수)와 상태 저장소가 필요하다는 결론에 도달했습니다.