쉘 스크립트 하나로 GAS 프로젝트, GitHub 저장소, 자동 배포 환경을 한 번에 구축할 수 있다. 이 환경만 갖추면 바이브 코딩의 준비는 끝이다.
나 혼자만의 2가지 선물이 있다. 하나는 템플릿 레포지토리이고, 하나는 쉘 프로그램이다.
템플릿 레포지토리는 앞서 아티클에서 설명했던 rules, workflows 뿐만 아니라 Google Apps Script 기본 설정들이 포함되어 있다.
쉘 프로그램은 수행을 하면 위의 레포지토리를 활용해서 본인의 Github 레포지토리와 구글 드라이브에 Google Apps Script 프로젝트를 자동으로 생성하게 도와주는 프로그램이다. 그리고 배포를 쉽게 하기 위해서 자동으로 Github과 clasp 를 연동시켜주기도 한다.
"명령어 하나로 GAS 프로젝트 + GitHub 저장소 + 자동 배포까지 다 만들어줌"
앞서서 우리는 모든 프로그램 및 환경 설정을 마무리 했다. 이제는 사용하면 된다.
./setup.sh my-awesome-gas
작업 디렉토리에 해당 쉘 프로그램을 위치시킨 후 실행 시키면 다음과 같이 사전 환경을 체크한 후 프로젝트 타입을 사용자에게 입력을 받는다.

프로젝트 선택
프로젝트를 선택하면 다음의 행동이 수행 된다. 위에서 말한 GAS 프로젝트 + GitHub 저장소 + 자동 배포까지 다 만들어주는 것이다.
GitHub 저장소 생성
템플릿에서 새 저장소 복사
자동으로 clone
GAS 프로젝트 생성
clasp create 실행
선택한 타입에 맞는 Code.js 생성
Script ID 추출
GitHub Secrets 자동 등록
SCRIPT_ID
CLASP_TOKEN_BASE64 (인증 토큰)
PROJECT_TYPE

프로젝트 생성 완료
결과는 구글 드라이브, Github 에 가면 확인할 수 있다.
참고로 프로젝트 선택 시 2번 스프레드시트를 선택했기 때문에 스프레드시트가 생긴다.
즉 Container-bounded 프로젝트가 생긴 것이고, 1번 독립형으로 선택을 하면 Apps Script 프로젝트가 드라이브에 생성된다.

구글 드라이브

Github
아래 스크립트에 대한 설명은 Antigravity 에 질문하면 더 쉽게 설명을 해 줄 것이다.
이 환경에서 바이브 코딩을 시작하려면?
이제 진짜 바이브코딩으로 Google Apps Script 프로젝트를 할 수 있는 모든 준비가 되었다.
다음 아티클은 실습 중심으로 프롬프트를 어떻게 작성해야 하는지 등에 대해서 정리할 예정이다. 스프레드시트에서 메일 발송하기가 첫 번째 실습이다.
#!/bin/bash
# ============================================
# GAS Unified Project Setup Script
# ============================================
# 사전 조건:
# - clasp login 완료
# - gh auth login 완료
# 사용법: ./setup.sh <project-name>
# ============================================
set -e # 에러 발생 시 즉시 중단
# 색상 정의
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 아이콘 정의
CHECK="✅"
CROSS="❌"
ROCKET="🚀"
GEAR="⚙️"
# 로그 함수
log_info() { echo -e "${BLUE}ℹ️ $1${NC}"; }
log_success() { echo -e "${GREEN}${CHECK} $1${NC}"; }
log_warning() { echo -e "${YELLOW}⚠️ $1${NC}"; }
log_error() { echo -e "${RED}${CROSS} $1${NC}"; }
log_step() { echo -e "\n${GEAR} ${YELLOW}$1${NC}"; }
# 헤더 출력
print_header() {
echo -e "${BLUE}"
echo "╔════════════════════════════════════════════╗"
echo "║ ${ROCKET} GAS Unified Project Setup ${ROCKET} ║"
echo "╚════════════════════════════════════════════╝"
echo -e "${NC}"
}
# 사용법 출력
print_usage() {
echo "사용법: $0 <project-name>"
echo ""
echo "예시:"
echo " $0 my-gas-project"
echo ""
echo "사전 조건:"
echo " - clasp login 완료 (clasp login)"
echo " - gh auth login 완료 (gh auth login)"
exit 1
}
# 사전 조건 확인
check_prerequisites() {
log_step "사전 조건 확인 중..."
local missing_deps=0
# gh CLI 확인
if ! command -v gh &> /dev/null; then
log_error "gh CLI가 없습니다."
missing_deps=1
else
# gh 인증 확인
if ! gh auth status &> /dev/null; then
log_error "gh 로그인이 필요합니다. (gh auth login)"
exit 1
fi
fi
# clasp 확인
if ! command -v clasp &> /dev/null; then
log_error "clasp이 없습니다."
missing_deps=1
else
# clasp 인증 확인
if [ ! -f ~/.clasprc.json ]; then
log_error "clasp 로그인이 필요합니다. (clasp login)"
exit 1
fi
fi
# jq 확인
if ! command -v jq &> /dev/null; then
log_error "jq가 없습니다."
missing_deps=1
fi
if [ $missing_deps -eq 1 ]; then
echo ""
log_warning "필수 도구가 설치되어 있지 않습니다."
echo "다음 명령어를 실행하여 자동으로 설치하세요:"
echo -e "${YELLOW} ./install-deps.sh${NC}"
exit 1
fi
log_success "모든 사전 조건이 충족되었습니다."
}
# 프로젝트 타입 선택
select_project_type() {
echo ""
echo -e "${BLUE}📦 프로젝트 타입 선택${NC}"
echo "────────────────────────────────────"
echo " 1) Standalone (독립형 - 웹앱, API, 라이브러리)"
echo " 2) Google Sheets (스프레드시트)"
echo " 3) Google Docs (문서)"
echo " 4) Google Forms (폼)"
echo " 5) Google Slides (프레젠테이션)"
echo ""
while true; do
read -p "선택 (1-5): " choice
case $choice in
1) PROJECT_TYPE="standalone"; break;;
2) PROJECT_TYPE="sheets"; break;;
3) PROJECT_TYPE="docs"; break;;
4) PROJECT_TYPE="forms"; break;;
5) PROJECT_TYPE="slides"; break;;
*) log_error "1-5 사이의 숫자를 입력하세요.";;
esac
done
log_success "선택된 타입: ${PROJECT_TYPE}"
}
# 타입별 Code.js 생성
create_code_template() {
local target_file="$1"
if [ "${PROJECT_TYPE}" == "standalone" ]; then
# Standalone 템플릿
cat > "${target_file}" << 'EOF'
/**
* @fileoverview Google Apps Script 메인 엔트리 포인트
* @description 이 템플릿은 clasp + GitHub Actions 자동 배포를 지원합니다.
*/
/**
* 스크립트 실행 시 호출되는 메인 함수
* GAS 에디터에서 직접 실행하거나 트리거로 호출할 수 있습니다.
*/
function main() {
Logger.log('🚀 GAS Template - Hello World!');
Logger.log('현재 시간: ' + new Date().toLocaleString('ko-KR'));
Logger.log('배포가 성공적으로 완료되었습니다.');
}
/**
* 웹 앱으로 배포 시 GET 요청 핸들러
* @param {Object} e - 이벤트 객체
* @returns {TextOutput} JSON 응답
*/
function doGet(e) {
const response = {
status: 'success',
message: 'GAS Template is running!',
timestamp: new Date().toISOString()
};
return ContentService
.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
}
/**
* 웹 앱으로 배포 시 POST 요청 핸들러
* @param {Object} e - 이벤트 객체
* @returns {TextOutput} JSON 응답
*/
function doPost(e) {
const response = {
status: 'success',
message: 'POST request received',
timestamp: new Date().toISOString()
};
return ContentService
.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
}
EOF
else
# Bounded 템플릿 (Sheets/Docs/Forms/Slides)
local ui_app=""
case ${PROJECT_TYPE} in
sheets) ui_app="SpreadsheetApp";;
docs) ui_app="DocumentApp";;
forms) ui_app="FormApp";;
slides) ui_app="SlidesApp";;
esac
cat > "${target_file}" << EOF
/**
* @fileoverview Google Apps Script Bounded Project 메인 엔트리 포인트
* @description Container-Bound 프로젝트용 템플릿 (${PROJECT_TYPE})
*/
/**
* 컨테이너 문서가 열릴 때 자동 실행되는 함수
* 커스텀 메뉴를 추가합니다.
*/
function onOpen() {
const ui = ${ui_app}.getUi();
ui.createMenu('📋 내 메뉴')
.addItem('기능 실행', 'myFunction')
.addSeparator()
.addIte
불러오는 중...