예약 관리 시스템이 필요해졌습니다. LINE Bot으로 예약을 받고, Stripe로 결제하고, Cloudflare Workers에서 Webhook을 받는 — 이 3계층 구성을 2주 동안 만들었습니다.
본 기사에서는 reserve-optimizer의 설계와 각 기술 선정 이유 및 구현 포인트를 설명합니다.
[LINE 사용자]
↓ 메시지
[Cloudflare Worker] ← 리버스 프록시
...
이 구성을 선택한 이유
- Cloudflare Workers: LINE Webhook의 HTTPS 필수 요건을 충족합니다 (GAS 단독으로는 느립니다).
- GAS: 서버가 필요 없고 무료 사용량으로 운영 가능하며 Google Sheets와 매끄럽게 연동됩니다.
- Stripe: PCI DSS 준수(카드 정보를 자체 시스템에 보유하지 않음)
// WebhookRouter.js — 타이밍 공격 방지 서명 검증
function verifyLineSignature(body, signature) {
const key = CryptoJS.enc.Base64.parse(getChannelSecret());
...
타이밍 공격: 문자열 비교 처리 시간으로 서명을 추측하는 공격입니다. === 대신 상수 시간 비교를 사용해야 합니다.
보충 설명: GAS (V8 런타임)에서는 Web Crypto API (crypto.subtle.timingSafeEqual)를 사용할 수 없기 때문에 CryptoJS에 의존합니다. Node.js 환경에서는 표준 라이브러리의 상수 시간 비교 함수가 이용 가능합니다.
// StripeWebhookHandler.js
function verifyStripeSignature(payload, sigHeader) {
const event = stripe.webhooks.constructEvent(
...
멱등성: Stripe는 동일한 이벤트를 여러 번 전송할 수 있습니다. CacheService (20분 TTL)로 중복을 제거합니다.
// worker.ts — 경로 기반 라우팅
export default {
async fetch(request: Request): Promise<Response> {
...
최소 전송: Workers는 수신한 파라미터를 URL 인코딩하여 GAS로 전송합니다. 페이로드 재구성을 최소화했습니다.
Stripe Checkout을 사용하는 이유: 카드 정보를 자체 시스템에 전혀 보유하지 않기 때문에 PCI DSS 대상에서 제외됩니다.
// 시트를 행 객체로 다루는 유틸리티
function findByColumn(sheet, column, value) {
const data = sheet.getDataRange().getValues();
...
// Bearer 토큰을 이용한 엔드포인트 인증
function doGet(e) {
const token = e.parameter.token;
...
주의: URL 파라미터에 토큰을 포함하기 때문에 접근 로그나 브라우저 기록에 남을 가능성이 있습니다. 실전 환경에서는 doPost + 헤더 인증을 권장하지만, 이번에는 GAS의 간이 검증 용도로 채택했습니다.
-
실행 시간 제한 6분: 긴 처리는 분할해야 합니다.
-
동시 실행 제한: 여러 Webhook이 동시에 도착할 경우 큐잉(queuing)이 필요합니다. → 본 프로젝트에서는 LockService.getScriptLock()으로 배타적 잠금 처리했습니다. 대규모 운영 시에는 Google Cloud Tasks 도입을 권장합니다.
-
스크립트 속성: 비밀 정보의 저장소 (암호화됨)
-
요청 크기 제한: LINE 이미지 메시지 등은 URL만 전송됩니다.
-
실행 시간 제한 30초: GAS 측 처리가 무거울 경우 타임아웃이 발생할 수 있습니다.
-
세션 유효 기간: 24시간 (기본값). 예약 시스템에서는 48시간으로 연장해야 합니다.
-
통화 설정:currency: 'jpy'를 빠뜨리기 쉽습니다.
| 대책 | 구현 상황 |
|---|
| LINE Webhook 서명 검증 | ✅ HMAC-SHA256 + 타이밍 안전성 |
| ... | |
- Webhook 서명 검증을 타이밍 안전하게 구현하기 — LINE/Stripe 서명 검증 상세 구현
- STRIDE 위협 모델링을 개인 개발에 도입하다 — 예약 시스템의 위협 분석 방법론
- Flask 상품 관리 구축 전 과정 기록 — 동일한 개인 개발 제품의 다른 사례
이 글은 Claude Code (GLM-5.1)와 함께 작성했습니다.
본 기사는 개인 Zenn에서도 공개하고 있습니다.