저는 구글 제품을 많이 사용하는 편입니다. 제 주력 캘린더도 Google 캘린더 고요. 이번에 모임의 임원을 맡게 되면서 회원들의 생일을 캘린더에 등록해야 할 일이 생겼어요. 그냥 하나하나 등록을 하는 도중 "내가 지금 뭐하고 있나.." 라는 자괴감이 들기 시작했어요.
구글 시트에 있는 날짜 정보(생일)을 한 번에 쉽게 일괄 등록할 수는 없을까라는 생각이 뇌리를 스쳤습니다. 그래서 찾아봤더니.. 약간의 매크로 프로그램을 작성하면 가능할 것 같더라고요.
그래서 열심히 개발을 해봤습니다.
1시간이면 등록할 것을 8시간 걸려서 프로그램을 짜 봤어요.
결과적으로는 더 비효율적이었네요. ㅠㅠ
그러나...
나에게는 비효율 적이었지만 이코드를 공개하면 다른 사람에게는 큰 도움이 될 수 있겠구나 생각을 하고 코드를 공개해 보려고 합니다.
준비물
준비물은 Google 스프레드시트, Google 캘린더만 있으면 돼요. 당연히 무료고요.
Google 캘린더
먼저 Google 캘린더를 만들거나 사용하고 있는 캘린더를 준비합니다.
- 적용하기 원하는 캘린더의 우측의
...
를 클릭하고 설정 및 공유
를 선택합니다.
- 캘린더 ID를 잘 기억해 놓습니다. 나중에 이 ID를 활용할 예정입니다.
Google 스프레드시트
Google 스프레드시트로 명단과 생일을 작성합니다.
▲ 위와 같이 작성을 하면 되고 중요한 사항은..
생년월일이 구글 시트의 날짜 형식에 맞아야 합니다. 그리고 갤린더등록, 캘린더상태의 항목은 필수로 있어야 합니다.
- 캘린더등록 : 캘린더에 등록할지 제거할지를 표시 (ADD / DEL)
- 캘린더상태 : 현재 캘린더에 해당 항목이 적용되었는지 확인 (Y / ' ')
매크로 프로그램 작성하기
기본적인 준비는 끝났습니다. 이제부터 Apps Script를 제작하고 트리거를 등록하면 됩니다.
Apps Script 작성하기
Apps Script는 구글 제품에 대해 javascript 문법으로 프로그램을 할 수 있게 만들어진 프로그램 언어입니다. 이 스크립트를 활용하여 매크로를 상세하게 제작할 수 있습니다.
- 캘린더의 상단 메뉴에서
확장 프로그램
을 선택합니다. Apps Script
를 선택합니다.
- 먼저 임의의 스크립트 이름을 작성합니다.
- 5개의 메뉴 중 2번째에 있는 편집기를 선택합니다.
- 기본으로 있는 함수을 사용해도 되지만 +를 눌러 새로운 함수를 만들어도 됩니다.
- 이름을 변경하여 특정 이름을 작성합니다.(기본 함수 이름을 사용해도 돼요) 이제 이 함수에 프로그램을 작성하면 되고 제가 작성한 코드를 복사 적용하시면 돼요.
전체 코드
/****************************************************************************************************
* 회원 생일을 캘린더에 자동등록하는 기능
****************************************************************************************************/
function goBirthCreate() {
/*****************************************************************************************************
************** 사용자가 등록해야 할 부분 *******************************************************
*****************************************************************************************************
* SheetTabName : 스프래드시트의 하단에 있는 Sheet tab의 이름
* Header ~~ : 스프래드시트의 Header명을 입력하면 해당 셀을 구글 캘린더에 등록함
* startRow : 처음 데이터가 시작하는 표의 시작점(row)
* startColumn : 처음 데이터가 시작하는 표의 시작점(column)
* calendarId : 등록하려는 캘린더에서 찾아서 작성함
* titlePrefaceWord : 캘린더에 등록할 때 제목부분에 공통으로 들어갈 머릿말
* descPrefaceWord : 캘린더에 등록할때 내용부분에 공통으로 들어가는 구분 단어임(캘린더이벤트 삭제할 때 필요하기 때문에 꼭 필요함)
* registYear : 캘린더에 등록할 연도
* alarm : 캘린더에 같이 등록할 알람(리마인더) 분 (분단위 숫자)
*****************************************************************************************************/
const SheetTabName = "회원생일";
const HeaderTitle = "성명";
const HeaderStartTime = "생년월일";
const HeaderDescription = "";
const HeaderEtc = "";
const HeaderRegYN = "캘린더등록";
const HeaderRegState = "캘린더상태";
const startRow = 3;
const startColumn = 2;
const calendarId = "su***************************lendar.google.com";
const titlePrefaceWord = "[테스트생일]";
const descPrefaceWord = "[구글시트_생일]";
const registYear = "2022";
const alarm1 = "10080"; // 첫번째 알람(분단위 숫자)
const alarm2 = "500"; // 두번째 알람(분단위 숫자)
/****************************************************************************************************/
/****************************************************************************************************/
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SheetTabName);
const eventCal = CalendarApp.getCalendarById(calendarId);
const endRow = spreadsheet.getLastRow();
const endColumn = spreadsheet.getLastColumn();
const count = spreadsheet.getRange(startRow, startColumn, endRow, endColumn).getValues(); // getRange(row, column, numRows, numColumns)
////////////////////////////////////////////////////////////////////////////////////////////////////
const colHeaderStartTime = spreadsheet.createTextFinder(HeaderStartTime).findNext().getColumnIndex() - startColumn;
const colHeaderTitle = HeaderTitle ? spreadsheet.createTextFinder(HeaderTitle).findNext().getColumnIndex() - startColumn : "";
const colHeaderDescription = HeaderDescription ? spreadsheet.createTextFinder(HeaderDescription).findNext().getColumnIndex() - startColumn : "";
const colHeaderEtc = HeaderEtc ? spreadsheet.createTextFinder(HeaderEtc).findNext().getColumnIndex() - startColumn : "";
const colHeaderRegYN = spreadsheet.createTextFinder(HeaderRegYN).findNext().getColumnIndex() - startColumn;
const colHeaderRegState = spreadsheet.createTextFinder(HeaderRegState).findNext().getColumnIndex() - startColumn;
/////////////////////////////////////////////////////////////////////////////////////////////////////
for (x = 0; x < count.length; x++) {
/**********************************************************************************************/
if (x === 15) Utilities.sleep(2 * 1000); // 한꺼번에 많은 캘린더를 등록하면 오류가 발생함
/**********************************************************************************************/
const shift = count[x];
const regYes = shift[colHeaderRegYN];
const title = shift[colHeaderTitle];
const description = shift[colHeaderDescription] ? shift[colHeaderDescription] : "";
const etc = shift[colHeaderEtc] ? "\n" + shift[colHeaderEtc] : "";
const titleSum = titlePrefaceWord + " " + title;
const descriptionSum = descPrefaceWord + " " + description + etc;
/***********************************************************************************************/
/***********************************************************************************************
* startTime은 캘린더에 등록할 년도는 금년 또는 내년이기 때문에 생일에서 년도는 빼고 지정한 년도로 교체해서 등록처리함
***********************************************************************************************/
// EST시간을 KOR시간으로 치환 시작
const KR_TIME_DIFF = 9 * 60 * 60 * 1000;
const startCurr = new Date(shift[colHeaderStartTime]);
const startUtc = startCurr.getTime() + startCurr.getTimezoneOffset() * 60 * 1000;
const startT = new Date(startUtc + KR_TIME_DIFF);
// EST시간을 KOR시간으로 치환 끝
const startTimeMonth = startT.getMonth();
const startTimeDay = startT.getDate();
const startCalendarTime = new Date(registYear, startTimeMonth, startTimeDay);
/***********************************************************************************************/
if (regYes === "DEL" || regYes === "del" || regYes === "D") {
const events = eventCal.getEventsForDay(startCalendarTime, { search: descPrefaceWord });
for (y = 0; y < events.length; y++) {
events[y].deleteEvent();
}
spreadsheet.getRange(Number(startRow + x), colHeaderRegYN + startColumn).setValue("");
spreadsheet.getRange(Number(startRow + x), colHeaderRegState + startColumn).setValue("");
} else if (regYes === "ADD" || regYes === "add" || regYes === "A") {
const event = {
description: descriptionSum,
guests: "",
};
if (titleSum !== null && titleSum !== "") {
const events = eventCal.getEventsForDay(startCalendarTime, { search: descPrefaceWord });
for (y = 0; y < events.length; y++) {
events[y].deleteEvent();
}
eventCal.createAllDayEvent(titleSum, startCalendarTime, event).addPopupReminder(alarm1).addPopupReminder(alarm2);
spreadsheet.getRange(Number(startRow + x), colHeaderRegYN + startColumn).setValue("");
spreadsheet.getRange(Number(startRow + x), colHeaderRegState + startColumn).setValue("Y");
}
}
}
}
function onOpenBirth() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("캘린더동기화").addItem("회원생일 업데이트", "goBirthCreate").addToUi();
}
부분 코드
코드는 크게 2개의 함수로 이루어져 있습니다.
function goBirthCreate() {}
function onOpenBirth() {}
goBirthCreate()
: 생일을 적용하는 코드onOpenBirth()
: 구글 시트에 적용 버튼이 나오게 하는 코드
코드 설정 영역
상단부분은 설정 부분입니다.
/*******************************************************************/
const SheetTabName = "회원생일"; // 스프래드시트의 하단에 있는 Tab의 이름
const HeaderTitle = "성명"; // [표의 Header 이름] 캘린더의 제목으로 등록될 항목
const HeaderStartTime = "생년월일"; // [표의 Header 이름] 캘린더의 등록될 날짜
const HeaderDescription = ""; // [표의 Header 이름] 캘린더의 내용으로 등록될 항목
const HeaderEtc = ""; // [표의 Header 이름] 캘린더의 내용을 등록될 항목
const HeaderRegYN = "캘린더등록"; // [표의 Header 이름] 캘린더에 등록할지 삭제할지 설정 (ADD/DEL)
const HeaderRegState = "캘린더상태"; // [표의 Header 이름] 현재 캘린더에 등록이 되어있는지 없는지 표시
const startRow = 3; // 실제 데이터가 시작하는 줄 번호
const startColumn = 2; // 실제 데이터가 시작되는 칸 번호
const calendarId = "sunrl******************************dar.google.com"; // 캘린더ID
const titlePrefaceWord = "[테스트생일]"; // 캘린더 제목에 등록할 말머리
const descPrefaceWord = "[구글시트_생일]"; // 캘린더 내용에 등록할 말머리
const registYear = "2022"; // 캘린더에 등록할 연도 설정
const alarm1 = "10080"; // 캘린더에 같이 등록할 첫번째 알람(분단위 숫자)
const alarm2 = "500"; // 캘린더에 같이 등록할 두번째 알람(분단위 숫자)
/******************************************************************/
트리거 등록하기
코드를 등록하고 설정했다면 이제 이 코드가 특정 상황에서 적용될 수 있게 트리거 코드를 작성하고 적용해야 합니다.
트리거 적용을 위한 코드
function onOpenBirth() {
const ui = SpreadsheetApp.getUi();
ui.createMenu("캘린더동기화").addItem("회원생일 업데이트", "goBirthCreate").addItem("회원생일 업데이트 2", "code").addToUi();
}
위와 같이 코드를 작성하면 아래와 같이 상단 메뉴에 버튼이 생성됩니다. 그리고 이 버튼을 클릭하면 프로그램이 실행돼서 캘린더에 반영이 됩니다.
.addItem()
을 붙여주면 계속 아래로 메뉴가 추가됩니다.
- 캘린더동기화 버튼이 보입니다.
- 회원생일 업데이트 버트이 보입니다.
트리거 반영하기
이제는 특정 이벤트가 발생할 때 onOpenBirth()
가 실행되는 트리거를 등록하면 됩니다.
- 좌측 메뉴 중
트리거
메뉴를 선택합니다. 트리거 추가
버튼을 클릭하여 새로운 트리거를 생성합니다.- 실행할 함수로 우리가 만든
onOpenBirth
를 선택합니다. - Head로 선택하면 됩니다.
스프레트시트
에서 발생하는 이벤트를 반영하겠다는 것입니다.- 스프레트시트가
열릴 시
이 함수를 실행하겠다는 뜻입니다. - 트리거가 실패하면
즉시 알림
입니다. 이제 해당 스프레드시트를 열게 되면 자동으로 상단 메뉴에 "캘린더동기화" 버튼이 보이게 됩니다. 생일을 추가하거나 삭제하거나 할 때 해당 버튼을 누르기만 하면 반영이 됩니다.
테스트해보기
실행해 보면 잘 반영되는 것을 볼 수 있습니다.