클로드 코드로 구글 캘린더 자동화 만든 이야기 — Apps Script + iPhone 단축어
터미널 하나에서 설계, 코드, 배포, 테스트까지 전부 끝낸 개발기

TL;DR
iPhone 단축어 + Google Apps Script + Google Calendar 조합으로 생리주기 자동 예측 시스템 구축
Claude Code 터미널 하나에서 설계, 코드 작성, clasp push, curl 테스트, 버그 수정, 캘린더 정리까지 전 과정 완결
CalendarApp 내장 API로 서버 없이 서버리스 함수 구현, 평균 주기 기반 3개월 예측 + undo 기능 설계
"해줘" 한 마디에서 동작하는 자동화까지, 바이브코딩 워크플로우의 실전 사례 정리

앱 깔기는 싫고, 캘린더에 버튼 하나로 넣고 싶었다
지난 글에서 이 자동화의 결과물을 소개했는데, 이번에는 어떻게 만들었는지 이야기를 해보려고 합니다.
시작은 단순했습니다. 생리주기를 기록하고 다음 달 예측까지 캘린더에 자동으로 넣고 싶었는데, 기존 생리주기 앱들은 너무 과했습니다. 광고가 붙어있거나, 건강 데이터를 수집하거나, 프리미엄 결제를 유도하거나. 원하는 건 딱 하나였습니다. 아이폰에서 버튼 하나 누르면 구글 캘린더에 기록되고, 다음 3개월치 예측이 자동으로 생기는 것.
그래서 Google Apps Script를 선택했습니다. 이유는 간단합니다.
CalendarApp.getCalendarById()한 줄이면 캘린더 접근 끝서버가 필요 없음 (Google이 무료로 호스팅)
웹앱으로 배포하면 URL 하나가 나오고, iPhone 단축어에서 HTTP POST 한 번이면 됨
서버 세팅, 인증 토큰 관리, 배포 파이프라인 같은 건 전부 필요 없습니다. 코드 하나 올리면 끝.
Claude Code에서 전부 다 했다
이 글의 핵심은 여기입니다. 설계부터 배포, 테스트, 디버깅까지 터미널 하나에서 Claude Code랑 대화하면서 전부 끝냈습니다.

"이거 만들어줘"에서 시작
Claude Code에 요구사항을 말하면 됩니다.
생리주기 트래커 만들어줘. iPhone 단축어에서 POST 보내면 Google Calendar에 실제 생리일 기록하고, 과거 데이터 기반으로 3개월치 예측 생성해줘. undo 기능도 필요해.
이러면 Claude Code가 설계를 잡고, Code.gs 파일을 작성합니다. 프로젝트 구조, 함수 분리, 설정값 분리까지 알아서 해줍니다.
clasp로 코드 푸시
Google Apps Script는 원래 브라우저의 스크립트 에디터에서 코드를 작성합니다. 그런데 clasp(Command Line Apps Script Projects)라는 CLI 도구가 있으면 로컬에서 작업한 코드를 바로 올릴 수 있습니다.
clasp login # Google 계정 인증 (처음 한 번)
clasp push --force # 로컬 Code.gs → Apps Script 프로젝트에 업로드
Claude Code가 코드를 수정하면, 바로 clasp push로 올리고, 테스트하고, 다시 수정하는 사이클이 터미널 안에서 완결됩니다.
curl로 바로 테스트
배포된 웹앱 URL에 curl을 쏘면 바로 결과를 확인할 수 있습니다.
curl -sL -d "" "https://script.google.com/macros/s/.../exec?secret=xxx&date=2026-03-10"
# 응답: OK: cycle=30, deleted=12
캘린더에 이벤트가 잘 생겼는지도 터미널에서 바로 확인합니다. Claude Code에서 Google Calendar MCP(Model Context Protocol)를 연결해뒀기 때문에 "3월 캘린더 이벤트 보여줘"라고 하면 바로 조회됩니다.
배포는 UI에서 해야 했던 삽질
하나 삽질한 게 있습니다. clasp deploy로 새 버전을 배포하려고 했는데, 개인 Google 계정(Workspace가 아닌)에서는 clasp의 자동 배포에 권한 제한이 있었습니다.
결국 코드 푸시는 clasp로 하고, 배포(웹앱 URL 갱신)는 Apps Script 에디터 UI에서 수동으로 했습니다. 한 번만 하면 되니까 큰 문제는 아니었지만, 이 부분은 Claude Code가 자동으로 해결해줄 수 없는 영역이었습니다.
테스트 데이터 정리도 Claude Code가
테스트하면서 캘린더에 쌓인 테스트 이벤트들이 있었습니다. 하트, 물방울 이벤트가 잔뜩 생겼는데, 이것도 Claude Code에서 Calendar MCP로 한 번에 정리했습니다. "3월에 생긴 테스트 이벤트들 다 지워줘"라고 하면 끝.
수정 -> push -> 테스트 -> 확인 -> 정리가 전부 터미널에서 끝나니까, 브라우저를 왔다갔다 할 필요가 거의 없었습니다.
동작 원리
전체 흐름은 이렇습니다.

버튼 하나 누르면 이런 일이 벌어집니다:
iPhone 단축어가 HTTP POST를 보냄 (오늘 날짜 포함)
Apps Script의
doPost()함수가 실행됨기존 예측 이벤트(분홍 하트, 물방울) 전부 삭제
실제 생리일을 빨간 하트로 4일간 기록
과거 빨간 하트 데이터에서 평균 주기를 계산
3개월치 예측 생성: 분홍 하트(예상 생리일), 물방울(배란 위험일)
주기 계산 로직이 이 시스템의 핵심입니다.
function calculateCycleLength(cal) {
const events = cal.getEvents(yearAgo, now);
// 1) 빨간 하트(실제 기록) 날짜만 수집
const periodDates = events
.filter(e => e.getTitle() === '❤️')
.map(e => dateOnly(e.getStartTime()));
// 2) 연속된 날짜를 그룹으로 묶어서 각 생리 시작일 추출
// (4일 연속 ❤️ = 하나의 생리 기간)
// 3) 최근 3회 주기의 평균 계산
const recent = cycles.slice(-3);
return Math.round(recent.reduce((a, b) => a + b, 0) / recent.length);
}
이 코드에서 핵심은 "연속된 날짜를 그룹으로 묶는" 부분입니다. 빨간 하트가 3월 14일, 3월 29일4월 1일 이렇게 찍혀 있으면, 7일 이상 간격이 나는 지점에서 끊어서 각각 하나의 생리 기간으로 인식합니다. 그래서 시작일 간의 간격 = 주기 길이가 됩니다.
실수로 잘못 기록했을 때를 위한 undo 기능도 있습니다.
function undoLastPeriod(cal) {
const groups = getRecentPeriodGroups(cal);
const lastGroup = groups[groups.length - 1];
// 가장 최근 기록 삭제
for (const item of lastGroup) {
item.event.deleteEvent();
}
// 예측 전부 삭제 후, 그 이전 기록 기준으로 재생성
deletePredictions(cal);
if (groups.length >= 2) {
generatePredictions(cal, prevStart, cycleLength);
}
}
잘못 눌렀으면 undo 단축어를 누르면 됩니다. 마지막 기록이 삭제되고, 그 이전 기록 기준으로 예측이 다시 생깁니다.
iPhone 단축어 연결
iPhone 쪽은 놀라울 정도로 간단합니다. 단축어 앱에서:
"URL의 콘텐츠 가져오기" 액션 추가
URL에 배포된 Apps Script 웹앱 주소 입력
메서드를 POST로 설정
쿼리 파라미터에
secret과date(현재 날짜) 추가
이걸 홈 화면에 위젯으로 올려두면, 버튼 한 번 탭으로 끝납니다.
배운 것
바이브코딩 관점
Claude Code + clasp 조합이 강력합니다. Apps Script 개발의 가장 큰 불편함은 브라우저 에디터인데, clasp이 있으면 로컬 파일로 작업하고 push만 하면 됩니다. Claude Code가 코드 작성과 push를 모두 할 수 있으니, "수정해줘 -> push해줘 -> 테스트해볼게 -> 이거 고쳐줘" 사이클이 매끄럽게 돌아갑니다.
MCP 연결이 디버깅을 바꿉니다. Calendar MCP가 연결되어 있으니 "방금 생긴 이벤트 보여줘", "테스트 데이터 지워줘" 같은 걸 대화로 처리할 수 있었습니다. 브라우저에서 캘린더 열어서 하나하나 확인하는 것과는 차원이 다릅니다.
개발 관점
Apps Script의 CalendarApp이 생각보다 강력합니다. OAuth 설정이나 API 키 관리 없이 CalendarApp.getCalendarById() 한 줄로 캘린더에 접근할 수 있습니다. 종일 이벤트 생성, 삭제, 조회가 전부 내장 메서드로 되니까 외부 라이브러리가 필요 없습니다.
undo 설계에서 "그룹핑"이 핵심이었습니다. 단순히 "마지막 이벤트 삭제"가 아니라 "마지막 생리 기간 전체 삭제 + 예측 재생성"을 해야 하니까, 연속된 날짜를 하나의 그룹으로 묶는 로직이 필요했습니다. 7일 이상 간격이 나면 다른 기간으로 판단하는 기준값은 실제 사용 패턴을 고려해서 정했습니다.
이 구조로 뭐든 만들 수 있다
iPhone 단축어에서 HTTP POST를 보내고, Apps Script가 받아서 Google Calendar(또는 Sheets)에 쓰는 패턴은 범용적입니다. 생리주기 트래커는 하나의 예시일 뿐이고, 같은 구조로 만들 수 있는 것들이 많습니다.
카페 도착 전 선주문 알림 (위치 기반 트리거 + 캘린더 메모)
비 올 때 우산 리마인더 (날씨 API + 캘린더 이벤트)
주차 타이머 (버튼 누르면 2시간 후 알림 이벤트 생성)
약 복용 기록 (매일 복용 여부를 Sheets에 체크)
서버도 필요 없고, 비용도 0원이고, Claude Code에 "이거 만들어줘"라고 하면 됩니다. 다음에는 뭘 자동화할지 고민 중입니다.