설 연휴를 맞아,
평소에 회사에서 이게 자동화가 되면 편할텐데 라고 생각했던 기능(?) 을 만들어보도록 하자!
1. 문제 상황: "가로로 끝없이 늘어나는 일정 시트"
우리 팀에서 외근 일정을 기록하기 위해 사용하는 구글 스프레드 시트는 날짜가 지나감에 따라 열(Column)이 오른쪽으로 계속 늘어나는 구조이다. 부장님이 직접 해당 인원의 일정을 칸에 추가해 주시는데, 이를 매번 확인해서 개인 구글 캘린더에 옮겨 적었었는데, 일정이 등록되면 알람이 오고, 캘린더에 자동으로 등록되면 정말 편하겠다는 생각이 들었다.
- 문제점 1: 공유 시트를 매번 직접 열어서 확인해야 함. (공유 시트의 내용 중 함부로 건드리면 안되는 탭들도 있음)
- 문제점 2: 캘린더에 수동으로 입력하다 보니 누락이나 오타 발생 가능성.
- 문제점 3: 회사 원본 시트에 직접 스크립트를 깔기에는 보안 및 안정성 우려.
2. 해결 전략: "보안과 편의성을 다 잡은 구조"
원본 데이터를 보호하면서 자동화 기능을 구현하기 위해 다음과 같은 구조를 설계하였다.

- 데이터 미러링: IMPORTRANGE 함수를 사용해 회사 시트의 내 일정만 개인 시트로 실시간 동기화.
- 변경 감지용 'Shadow' 시트: 수식이 아닌 '값'만 저장하는 별도 시트를 두어, 1분 전 데이터와 현재 데이터를 비교해 변경 사항 포착.
- 알림 및 동기화: 변화가 감지되면 즉시 디스코드 웹후크로 알림을 쏘고, 구글 캘린더에 이벤트를 자동 생성.
3. 상세 구현 과정
단계 1: 개인 전용 시트 생성 및 데이터 연동
원본 시트의 특정 행(내 일정 영역)만 가져오도록 함수를 설정한다.
구글 스프레드시트를 새로 만들고, 아래 함수를 입력해준다.
={IMPORTRANGE("원본_URL", "시트명!LK1:ZZ1"); IMPORTRANGE("원본_URL", "시트명!LK43:ZZ48")}
이렇게 하면 이번 주 이후의 날짜와 내 일정만 싹 긁어올 수 있다.
참고로 시트명 옆의 주소는 내 칸이 변경되면 변경해야 하는 값이다.
단계 2: 구글 앱스 스크립트(GAS) 코딩
핵심 로직은 Shadow 시트와 현재 시트의 비교. IMPORTRANGE는 시트 편집으로 인식되지 않기 때문에, 1분마다 스냅샷을 찍어 비교하는 방식이 가장 확실하다고 생각했다.(구글 서버니까 1분 씩 돌려~)
- 날짜 파싱: "2월16일~2월20일" 같은 텍스트 주차 정보를 분석해 월~금 요일에 맞는 정확한 날짜(Date 객체)로 변환하는 로직 구현.
- 중복 방지: 이미 확인된 내용은 Shadow 시트에 저장하여 다음 실행 시 중복 알림이 가지 않도록 설정.
새로 생성한 시트에서, [확장 프로그램] > [App Scripts] 로 이동한 후, 편집기 탭에서 Code.gs 에 자동화 스크립트 코드를 작성한다.
const DISCORD_URL = "디스코드 웹 후크 URL";
function checkChangesAndNotify() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const mainSheet = ss.getSheets()[0];
const shadowSheet = ss.getSheetByName('Shadow') || ss.insertSheet('Shadow');
const lastCol = mainSheet.getLastColumn();
if (lastCol === 0) return;
// Shadow 시트 열 부족 방지
if (shadowSheet.getMaxColumns() < lastCol) {
shadowSheet.insertColumnsAfter(shadowSheet.getMaxColumns(), lastCol - shadowSheet.getMaxColumns());
}
// 데이터 가져오기 (1행:날짜, 2~6행:월~금)
const mainData = mainSheet.getRange(1, 1, 6, lastCol).getValues();
const shadowData = shadowSheet.getRange(1, 1, 6, lastCol).getValues();
let hasChanges = false;
for (let col = 0; col < lastCol; col++) {
let weekText = mainData[0][col];
if (!weekText || weekText.toString().trim() === "") continue;
for (let row = 1; row < 6; row++) {
let currentVal = mainData[row][col] ? mainData[row][col].toString().trim() : "";
let previousVal = (shadowData[row] && shadowData[row][col]) ? shadowData[row][col].toString().trim() : "";
// 내용이 있고 이전 값과 다르면 알림 발송
if (currentVal !== "" && currentVal !== previousVal) {
const dateInfo = calculateDate(weekText, row);
// 요청하신 새로운 텍스트 포맷으로 발송
sendDiscord(dateInfo.formatted, currentVal);
// 구글 캘린더 등록
CalendarApp.getDefaultCalendar().createAllDayEvent(`[솔리드] ${currentVal}`, dateInfo.rawDate);
hasChanges = true;
}
}
}
// 변경사항 저장
if (hasChanges) {
shadowSheet.getRange(1, 1, 6, lastCol).setValues(mainData);
}
}
// 날짜 계산 함수
function calculateDate(weekText, row) {
const dayNames = ["월요일", "화요일", "수요일", "목요일", "금요일"];
const dayName = dayNames[row - 1];
try {
let startDateStr = weekText.split('~')[0];
let month = parseInt(startDateStr.split('월')[0]);
let day = parseInt(startDateStr.split('월')[1].replace('일', ''));
// 2026년 기준 요일 계산
let targetDate = new Date(2026, month - 1, day + (row - 1));
return {
formatted: `${month}월 ${targetDate.getDate()}일 ${dayName}`,
rawDate: targetDate
};
} catch (e) {
return { formatted: `날짜 확인 불가 (${dayName})`, rawDate: new Date() };
}
}
// 변경된 디스코드 전송 함수
function sendDiscord(dateStr, content) {
const message = `**구글시트 일정이 등록되었습니다**\n\n일시 : ${dateStr}\n내용 : ${content}`;
const payload = {
"content": message
};
UrlFetchApp.fetch(DISCORD_URL, {
"method": "post",
"contentType": "application/json",
"payload": JSON.stringify(payload)
});
}
단계 3: 시간 기반 트리거 설정
스크립트가 잠들지 않고 일하도록 [분 단위 타이머 - 1분마다] 실행 트리거를 설정. 이제 제가 컴퓨터를 꺼도 구글 서버가 24시간 감시를 수행한다.
4. 최종 결과물 (디스코드 알림 포맷)
위와 같이 세팅하고 팀에서 사용하는 스프레드시트에 내용을 입력하면, 트리거의 실행 주기에 맞춰서(현재 1분) 다음과 같이 디스코드 알림이 온다.

알림 봇의 이미지는 다른 알림 봇과 햇갈리지 않게 회사 로고로 넣어주었다.
동시에 구글 캘린더에도 해당 날짜에 일정이 자동으로 입력된다.

5. 마무리
이제 부장님께서 내 일정을 등록 해주시면 1분안에 디스코드 알림이 온다. 그러면 따로 스프레드시트를 확인할 것 없이 구글 캘린더로 가서 일정을 (외근, 정기점검, 연차, 내부업무) 카테고리에 맞게 분류하고 상세 시간을 수정 해 주기만 하면 된다.
구글 캘린더에 외근일정, 정기점검일정, 연차 등 더 세분화 하여 등록이 되도록 하면 좋을 것 같은데, 그래도 이 정도 분류와 세세한 수정은 직접 하면서 일정에 대해 복기하기로 했다.(기술적 포기 아님).
앞으로도 업무에 있어서 혹은 다른 개인적인 효율화를 위해 자동화 할 것들을 찾아서 시도 해봐야겠다.
'CS 공부 > 기타' 카테고리의 다른 글
| [ETC]시스템 리소스 수집 방식 비교 (0) | 2026.01.05 |
|---|---|
| [ETC] MobaXterm으로 Jump Host + SSH 포트포워딩 설정하기 (0) | 2025.10.14 |
| [ETC] OS 설치 트러블슈팅 기록 ( RAID 구성이 안될 때) (0) | 2025.09.30 |
| [ETC] QoS(Quality of Service)란 무엇인가? (0) | 2025.09.16 |
| [ETC] 긴 프로세스 명령어, systemd 서비스로 정의하여 사용하기 (0) | 2025.09.08 |