*본 게시물은 지피터스 AI게시글 서포터 GPT를 활용하여 작성하였습니다.
(텍스트는 수정 없이 위 GPT가 제공해준 원문을 그대로 사용했고, 링크 및 이미지 삽입만 추가 했습니다.)
소개
- 구글 시트의 데이터 업데이트를 자동화하여 이메일 알림 전송 및 구글 캘린더 이벤트 생성을 목표로 했습니다.
- 처음에는 제미나이(Gemini)를 통해 자동화를 시도했으나, 해당 요구사항을 지원하지 않아 Google Workspace 환경에 적합한 앱스크립트를 추천받아 활용하게 되었습니다.
진행 방법
사용 도구: 구글 앱스크립트(Apps Script)
사용한 스크립트의 주요 기능:
K열 (Next Activity Date)가 수정되면 구글 캘린더에 새로운 이벤트를 생성합니다.
E열 (Stage)가 수정되면 지정된 이메일로 알림을 전송합니다.
주요 프롬프트
아래 구글 시트를 다음 조건을 만족하도록 apps script 코드를 작성해줘.
(코드뿐 아니라 환경 설정에 대해서도 최대한 단계적으로 쉽게 설명)
1. Deals 시트의 'Next Activity Date'가 업데이트되면 내 Google Calendar 에 해당 일자로 등록해주고, 캘린더 discription 엔 해당 Contact 및 Notes 의 내용을 작성해줘. 캘린더 알림은 기본적으로 1시간전으로 설정해줘.
2. Deals 시트의 'Stage'가 업데이트되면, 내가 지정한 이메일 주소로 메일을 발송해줘. 각 단계를 이메일 제목으로 하고, 해당 단계에 따른 격려 메시지와 핵심이 되는 액션아이템 제시를 본문으로 해줘
*apps script 코드 작성을 위해 추가적으로 더 필요한 정보가 있으면 내게 요청해~
---
구글 시트 링크:
https://docs.google.com/spreadsheets/d/1sIXr7dz8GfUaCCfht2V3D8iATozlfzYwq_vjA_4G2hE/edit?usp=sharing
+ 구글 시트 링크를 읽어 오지는 못하여, Deals 시트의 구조를 최대한 자세히 설명했습니다.
(지금부터는... 무려 2시간의 시행착오를 통해🥲😫 인내를 배우고 앱스크립트 기본기까지 마스터하게 되는 과정!)
마침내...🫥🫨😇 전체 진행 프로세스와 함께 최종 구글 앱스크립트의 코드를 보여 드릴 수 있게 되었습니다!!
전체 프로세스
Google Sheet 설정
대상 Sheet 이름: DEALS
주요 열:
K열 (Next Activity Date): 수정 시 캘린더 이벤트 생성.
E열 (Stage): 수정 시 이메일 전송.
B열 (Contact): 이벤트나 이메일에 사용될
Contact
정보.L열 (Notes): 이벤트 설명에 사용될 추가 정보.
Apps Script 설정
Google Sheets 상단 메뉴에서 확장 프로그램 > Apps Script를 클릭.
열리는 Apps Script 창에서 아래 코드를 복사하여 붙여넣기.
function onEdit(e) {
try {
if (!e || !e.range) {
Logger.log("No edit event object found. Make sure the event is triggered from Google Sheets.");
return;
}
const sheet = e.source.getActiveSheet();
const range = e.range;
const row = range.getRow();
const col = range.getColumn();
Logger.log(`Edit detected in sheet: ${sheet.getName()}, Row: ${row}, Column: ${col}`);
if (sheet.getName() !== "DEALS") {
Logger.log(`This edit is not in the 'DEALS' sheet. Current sheet: ${sheet.getName()}`);
return;
}
const nextActivityDateCol = 11; // K열 (Next Activity Date)
const stageCol = 5; // E열 (Stage)
// K열 (Next Activity Date) 수정 시
if (col === nextActivityDateCol && row >= 12) {
const nextActivityDate = sheet.getRange(row, nextActivityDateCol).getValue();
const contact = sheet.getRange(row, 2).getValue(); // B열 (Contact)
const notes = sheet.getRange(row, 12).getValue(); // L열 (Notes)
if (nextActivityDate) {
Logger.log(`Adding calendar event: Contact=${contact}, Date=${nextActivityDate}, Notes=${notes}`);
addEventToCalendar(nextActivityDate, contact, notes);
} else {
Logger.log("No Next Activity Date provided. Skipping calendar update.");
}
}
// E열 (Stage) 수정 시
if (col === stageCol && row >= 12) {
const stage = sheet.getRange(row, stageCol).getValue();
const contact = sheet.getRange(row, 2).getValue(); // B열 (Contact)
const company = sheet.getRange(row, 1).getValue(); // A열 (Company)
Logger.log(`Stage updated to: ${stage}`);
sendEmailNotification(stage, contact, company);
}
} catch (error) {
Logger.log(`Error in onEdit: ${error.message}`);
}
}
// Google Calendar에 이벤트 추가 (오전 9시 고정)
function addEventToCalendar(date, contact, notes) {
try {
const calendar = CalendarApp.getDefaultCalendar(); // 기본 캘린더 사용
const startTime = new Date(date);
startTime.setHours(9, 0, 0, 0); // 오전 9시 고정
const endTime = new Date(startTime.getTime() + 3600000); // 1시간 후 종료
const event = calendar.createEvent(
Follow-up with ${contact},
startTime,
endTime,
{
description: Contact: ${contact}\nNotes: ${notes},
reminders: [{ method: 'popup', minutes: 60 }]
}
);
Logger.log(`Calendar event created: ${event.getTitle()} on ${event.getStartTime()}`);
} catch (error) {
Logger.log(`Error in addEventToCalendar: ${error.message}`);
}
}
// 이메일 전송
function sendEmailNotification(stage, contact, company) {
try {
const email = "[email protected]"; // 이메일 주소
const subject = Stage Update: ${stage} - ${contact} - ${company};
let message = "";
// 단계별 액션아이템 문구 작성
switch (stage) {
case "Lead":
message = "리드 단계: 영업팀에 연락하여 첫 미팅을 준비하세요.";
break;
case "Opportunity":
message = "기회 단계: 고객과의 관계를 강화하고 니즈를 파악하세요.";
break;
case "Demo":
message = "데모 단계: 데모 준비를 위해 개발팀에 의뢰하세요.";
break;
case "Quote":
message = "견적 단계: 재무팀에 고객이 거절할 수 없는 견적서 작성을 요청하세요.";
break;
case "Closed Won":
message = "축하합니다! 계약이 체결되었습니다. 다음 단계를 진행하세요.";
break;
case "Closed Lost":
message = "계약이 종료되었습니다. 이유를 분석하고 향후 전략을 수정하세요.";
break;
default:
message = "단계가 업데이트되었습니다. 다음 단계를 준비하세요.";
break;
}
GmailApp.sendEmail(email, subject, message);
Logger.log(`Email sent to ${email} with subject: "${subject}"`);
} catch (error) {
Logger.log(`Error in sendEmailNotification: ${error.message}`);
}
}
트리거 설정
Apps Script 편집기에서 왼쪽 시계 모양 아이콘 (Triggers) 클릭.
+ Add Trigger 버튼 클릭.
설정:
Function to run:
onEdit
Event source:
From spreadsheet
Event type:
On edit
저장 후 트리거가 설정되었는지 확인.
권한 승인 요청
스크립트를 처음 실행하면 Google 권한 승인이 필요합니다.
표시되는 승인 화면에서
고급 > 안전하지 않은 페이지로 이동
을 클릭한 후 승인하세요.
테스트 방법
Google Sheets로 이동하여 DEALS 시트를 엽니다.
K열 (
Next Activity Date
):날짜를 수정하거나 추가합니다.
Google Calendar에서 이벤트가 생성되었는지 확인합니다.
E열 (
Stage
):단계를 변경합니다.
[email protected]
이메일로 알림이 수신되었는지 확인합니다.
Apps Script 편집기에서 Execution Logs를 확인하여 스크립트 실행 결과를 점검합니다.
결과가 나오지 않을 경우
트리거 문제: 트리거 설정이 올바른지 다시 확인합니다.
Execution Logs: 실행 로그를 확인하여 오류 메시지를 기반으로 문제를 해결합니다.
결 과와 배운 점
배운 점: 구글 앱스크립트를 통해 Google Workspace 환경에서 데이터 변경을 자동으로 처리하는 간단한 방법을 체득했습니다. + 이젠 n8n도 잘 할 수 있을 것 같은 자신감 한스푼🥄
실패담: 처음에 제미나이를 선택했지만, 해당 요구사항을 해결하지 못해 앱스크립트로 변경했습니다.
계획: 앞으로 다른 Google Workspace 도구와도 모두 다 연동하여 자동화 워크플로를 확장할 예정입니다.