콘텐츠로 이동

20. 웹앱 자동화 가이드 - Google Apps Script vs Cloudflare Workers

핵심: "무료로 빠르게 만들기"는 Apps Script, "빠르고 전문적으로 운영하기"는 Cloudflare Workers. 프로토타입 → Apps Script, 실서비스 → Cloudflare가 가장 효율적인 순서.


언제 무엇을 쓰는가

"웹으로 자동화하고 싶다"
  |
  ├─ 구글 시트/폼 기반이면 충분한가?
  |     ├─ YES → Google Apps Script
  |     └─ NO → Cloudflare Workers
  |
  ├─ 외부 사용자(고객)가 접근하는가?
  |     ├─ YES + 속도/URL 중요 → Cloudflare Workers
  |     └─ YES + 속도 괜찮음 → Apps Script도 가능
  |
  └─ 커스텀 도메인이 필요한가?
        ├─ YES → Cloudflare Workers
        └─ NO → Apps Script

상세 비교

항목 Google Apps Script Cloudflare Workers
비용 완전 무료 무료 (일 10만 요청까지)
로딩 속도 3~10초 (콜드 스타트) 0.1~0.5초
데이터 저장 구글 시트 (내장) D1 (SQLite DB)
이메일 MailApp 한 줄 Resend API 연동 필요
SMS 외부 API (알리고 등) 동일
캘린더 CalendarApp 한 줄 Google Calendar API 연동
URL script.google.com/macros/s/매우긴ID/exec 커스텀 도메인 가능
디자인 제한적 (iframe 안에 렌더링) 완전 자유
배포 배포 관리 > 새 버전 wrangler deploy
코드 수정 브라우저 편집기에서 복사/붙여넣기 로컬 파일 수정 > 명령어 배포
인증 구글 계정 또는 PIN 자유롭게 구현
개발 난이도 중 (JavaScript) 중~상 (TypeScript)

Google Apps Script 핵심 정리

구조

구글 시트 (데이터 저장)
  |
  └─ Apps Script (코드 실행)
        ├─ doGet(e) ─────── 웹 페이지 서빙
        ├─ 서버 함수들 ────── 비즈니스 로직
        └─ HTML 파일들 ───── 프론트엔드 (google.script.run으로 서버 호출)

핵심 패턴

1. 웹앱 라우팅 (URL 파라미터로 페이지 분기)

function doGet(e) {
  var page = e.parameter.page || 'main';
  var template = HtmlService.createTemplateFromFile('Page' + page);
  return template.evaluate();
}

2. HTML에서 서버 함수 호출

// 프론트엔드 (HTML)
google.script.run
  .withSuccessHandler(function(result) { /* 성공 */ })
  .withFailureHandler(function(err) { /* 실패 */ })
  .서버함수이름(인자1, 인자2);
- 비유: "서버야, 이 함수 실행하고 결과 알려줘" - 장점: REST API 설계 없이 함수 이름으로 바로 호출 - 단점: 느림 (매 호출마다 서버 왕복)

3. 트리거 (자동 실행) - 폼 제출 시: onFormSubmitHandler - 시트 수정 시: onEditHandler - 시간 기반: 6시간마다 체크 (미응답 알림 등)

4. 내장 서비스

MailApp.sendEmail({to, subject, body});     // 이메일
CalendarApp.createEvent(title, start, end);  // 캘린더
SpreadsheetApp.getActiveSheet();             // 시트

주의사항

  • 시간 값 자동 변환: "14:00" 입력하면 시트가 Date 객체로 변환해버림 → setNumberFormat('@') 텍스트 강제
  • 콜드 스타트: 오래 안 쓰면 첫 로딩 10초 이상
  • 배포 시 버전: 코드 수정 후 반드시 "새 버전"으로 배포해야 반영
  • URL 길이: 매우 길고 지저분함, 커스텀 도메인 불가

Cloudflare Workers 핵심 정리

구조

Cloudflare Edge (전 세계 300+ 데이터센터)
  |
  ├─ Workers (서버 코드, TypeScript)
  |     └─ Hono (경량 웹 프레임워크)
  |
  ├─ D1 (SQLite 데이터베이스)
  |
  ├─ Assets (정적 파일: HTML/CSS/JS)
  |
  └─ 외부 연동
        ├─ Resend (이메일 API)
        ├─ 알리고 (SMS API)
        └─ Google Calendar API

핵심 패턴

1. Hono 라우팅 (REST API)

const app = new Hono();

app.get('/api/data/:id', async (c) => {
  const id = c.req.param('id');
  const row = await c.env.DB.prepare('SELECT * FROM table WHERE id=?').bind(id).first();
  return c.json({ success: true, data: row });
});

app.post('/api/action', async (c) => {
  const body = await c.req.json();
  // 처리...
  return c.json({ success: true });
});

2. 프론트엔드에서 API 호출 (fetch)

const resp = await fetch('/api/register', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name, phone, email })
});
const result = await resp.json();

3. D1 데이터베이스

// 읽기
const row = await c.env.DB.prepare('SELECT * FROM requests WHERE id=?').bind(id).first();
// 쓰기
await c.env.DB.prepare('INSERT INTO requests (name, phone) VALUES (?, ?)').bind(name, phone).run();
- 비유: 구글 시트 대신 진짜 데이터베이스. 속도가 훨씬 빠름

4. 환경 변수 & 시크릿

# wrangler.toml (공개 설정)
[vars]
COMPANY_NAME = "우리집인테리어디자인"
PIN = "0000"
# 시크릿 (API 키 등)
wrangler secret put RESEND_API_KEY

Apps Script → Cloudflare 이전 체크리스트

  • [ ] google.script.run → fetch('/api/...') 변환
  • [ ] MailApp → Resend API
  • [ ] CalendarApp → Google Calendar API (서비스 계정)
  • [ ] SpreadsheetApp → D1 (SQL)
  • [ ] doGet 라우팅 → Hono 라우팅
  • [ ] 시간 트리거 → Cron Triggers (wrangler.toml에 설정)
  • [ ] HTML 템플릿 () → URL 파라미터 + JS로 처리

이메일 발송 비교

Apps Script

MailApp.sendEmail({ to: 'user@email.com', subject: '제목', body: '내용' });
// 끝. 한 줄.
// 발신: 구글 계정 이메일로 자동 발송
// 한도: 무료 계정 100통/일

Cloudflare + Resend

await fetch('https://api.resend.com/emails', {
  method: 'POST',
  headers: { Authorization: 'Bearer API키', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    from: '우리집인테리어디자인 <noreply@medinterior.com>',  // 커스텀 발신 주소!
    to: ['user@email.com'],
    subject: '제목',
    text: '내용'
  })
});
// 장점: 커스텀 도메인 발신, HTML 이메일, 발송 추적
// 무료: 100통/일, 월 3,000통

실전 교훈 (미팅 예약 시스템에서 배운 것)

1. 프로토타입 먼저

  • Apps Script로 기능/플로우 검증 → 확정된 후 Cloudflare로 이전
  • "작동하는 프로토타입"이 있으면 이전할 때 스펙이 명확해서 훨씬 빠름

2. 구글 시트의 함정

  • 시트에 "14:00" 입력 → Date 객체로 자동 변환 → "Sat Dec 30 1899 14:00:00" 출력
  • 해결: setNumberFormat('@') (텍스트 강제) + 읽을 때 Date instanceof 체크

3. URL 설계가 UX

  • Apps Script: script.google.com/macros/s/AKfyc.../exec?page=customer&id=5&token=abc
  • Cloudflare: woori-meeting.medinterior.com/customer/5/abc
  • 고객이 보는 URL이 깔끔해야 신뢰감

4. 콜드 스타트 체감

  • Apps Script: 3~10초 (첫 로딩). 자주 쓰면 줄어들지만, 고객이 처음 접하는 순간은 항상 느림
  • Cloudflare Workers: 0.1~0.5초. 체감 차이가 큼

5. 메시지 톤은 코드보다 중요

  • 코드를 다 만든 후에도 메시지 톤 조정에 시간이 많이 들었음
  • "맞춰드리겠습니다" → "최대한 조율해보겠습니다" (약속 vs 노력)
  • "추후 안내" → 빈칸 (없는 정보는 안 보여주는 게 낫다)
  • 메시지는 별도 텍스트 파일로 관리 → 비개발자가 직접 검토/수정 가능

비용 정리

서비스 무료 한도 유료 시작
Apps Script 이메일 100통/일, 트리거 20개 거의 안 넘음
Cloudflare Workers 10만 요청/일 $5/월
Cloudflare D1 500만 행 읽기/일, 10만 쓰기/일 $0.75/100만
Resend 100통/일, 월 3,000통 $20/월 (5만통)
알리고 SMS 없음 (선불) 건당 8~25원

소규모 비즈니스는 전부 무료 티어 안에서 운영 가능.