구글시트 대시보드 자동 갱신 방법: 여러 뷰 한 번에 돌리고 트리거로 자동화하기
도입: 트리거 중복과 6분 한도를 한 번에 정리하기
구글시트 대시보드 자동 갱신 방법을 찾다 보면, 대부분 “각 뷰마다 함수 하나씩 만들고 버튼을 단다”는 수준에서 멈추는 경우가 많습니다. 일일 예약, 상태별 현황, 야드 트레일러처럼 뷰가 세 개 이상으로 늘어나면 버튼도 세 개, 시간 트리거도 세 개가 됩니다. 그 결과 같은 함수에 시간 트리거가 여러 개 겹치고, 세 함수가 한꺼번에 돌면서 Google Apps Script 6분 실행 한도에 걸려 멈추는 문제가 반복됩니다.
이 글에서는 이미 구글시트 예약 현황 자동화 방법: 오늘 시트 만들기 — 대시보드 1편, 구글시트 예약 상태별 현황 대시보드 만들기 — 대시보드 2편, 구글시트 야드 트레일러 현황 만들기 — 대시보드 3편 (수정·완성 버전)까지 구현되어 있다는 전제로 설명합니다. 그 위에 DASH_refreshAllViews 하나로 세 뷰를 한 번에 갱신하고, DASH_resetRefreshTrigger 로 시간 트리거를 “항상 1개만 유지”하는 구조를 만드는 것이 핵심입니다.
저는 실제 현장에서 예약·체크인·야드 현황을 한 화면에서 보기 위해 같은 문제를 겪었습니다. 각각의 시트에서 수동으로 새로 고침하거나, 함수마다 트리거를 따로 걸어 두는 방식은 일정이 조금만 꼬여도 관리가 어려웠습니다. 그래서 “대시보드용 갱신 함수는 하나, 그 함수에 연결된 시간 트리거도 하나”라는 원칙으로 지금 구조를 정리했습니다.
전체 구조: 한 함수로 세 뷰를 묶고, 요약 숫자도 같이 준비하기
이번 편에서 구현하는 구성 요소는 크게 두 가지입니다.
첫째, DASH_refreshAllViews() 입니다. 앞 편에서 만든 세 가지 함수 DASH_updateApptDailyView(), DASH_updateApptStatusView(), DASH_updateYardStatusView() 를 이 함수 하나 안에서 순서대로 호출해 “구글시트 여러 시트 한 번에 갱신”을 처리합니다. Apps Script 트리거에서는 이 함수만 호출하도록 만들어, 시간 트리거가 여러 개로 흩어지지 않도록 합니다. 이때 DASH_updateApptDailyView 는 숫자(갱신된 예약 건수 등)를 반환하고, DASH_updateApptStatusView 와 DASH_updateYardStatusView 는 숫자 또는 undefined 를 반환한다는 것을 전제로 코드를 구성합니다.
둘째, DASH_getDashboardData() 입니다. TODAY 시트에 이미 계산해 둔 내용을 빠르게 훑어 오늘 예약 수, 야드 트레일러 수, 갱신 시각 같은 간단한 요약 데이터를 객체로 돌려줍니다. 이 함수는 웹앱, 내부 포털, 다른 보고서 시트에서 “구글시트 대시보드 자동화”를 확장할 때 재사용하기 좋습니다. 이 함수에 대해서는 간단한 테스트 함수에서 기대값(날짜 형식·숫자 여부)을 검증하는 코드도 같이 만들어 확인합니다.
이번 글의 코드는 모두 DASH_와 DASH_CONFIG4 접두어를 사용합니다. 같은 프로젝트 안에 앞 편 코드까지 합쳐 넣었을 때도 상수·함수 이름이 겹치지 않도록 하기 위한 설계입니다. 또한 TODAY 시트 구조가 회사마다 다를 수 있기 때문에, DASH_getDashboardData() 부분은 “예시 집계”로 보시고 실제 열 배치에 맞게 수정하는 것을 전제로 합니다.
대시보드 공용 설정: 시트·시간·트리거 이름을 한곳에 모으기
여러 곳에서 반복해서 쓰는 값은 상수로 모아 두는 것이 안정적입니다. 시트 이름, 허용 실행 시간, 트리거로 호출할 함수 이름을 한 번에 바꿀 수 있어야 운영 중 수정이 편합니다.
1) 이 코드가 하는 일
- 대시보드 관련 공용 설정을 상수(
DASH_CONFIG4)에 모아 둡니다.
2) 붙여넣을 위치
구글시트 → 확장 프로그램 → Apps Script → Code.gs맨 위(다른 상수들보다 아래여도 무방합니다).
3) 붙여넣은 뒤 할 일
- 저장만 하면 되고, 별도 실행은 필요 없습니다.
// 대시보드 공용 설정 상수 // → 이 편에서만 사용하는 설정
const DASH_CONFIG4 = { // → 대시보드 4편 전용 설정
TODAY_SHEET_NAME: 'TODAY', // → 대시보드용 시트 이름
MAX_REFRESH_MINUTES: 5, // → 전체 갱신 허용 시간(분)
TIMEZONE: 'America/New_York', // → 공통 시간대
TRIGGER_FUNCTION_NAME: 'DASH_refreshAllViews', // → 트리거 대상 함수명
};제대로 됐는지 확인하는 법: Apps Script 편집기에서 저장했을 때 에러 표시가 없으면 정상입니다.
세 뷰 한 번에 갱신하기: 잠금과 실행 시간 한도를 함께 고려하기
세 뷰를 한 번에 돌리려면 두 가지 위험을 먼저 막아야 합니다. 하나는 같은 시간에 트리거·수동 실행이 겹쳐 TODAY 시트를 동시에 쓰는 경우이고, 다른 하나는 작업량이 많을 때 6분 실행 한도를 넘어가는 경우입니다.
그래서 DASH_refreshAllViews() 에서는 진입 시 LockService.getScriptLock() 으로 잠금을 걸고, 잠금을 얻지 못하면 바로 빠져나가도록 합니다. 또한 각 뷰를 실행하기 전에 “남은 시간”을 계산해, 허용 시간(MAX_REFRESH_MINUTES)에 임박하면 남은 뷰는 건너뛰고 경고 로그만 남깁니다. 이렇게 하면 구글시트 대시보드 자동 갱신 중에도 실행 한도를 넘지 않도록 방어할 수 있습니다.
1) 이 코드가 하는 일
- 스크립트 잠금을 걸고, 세 뷰를 순서대로 갱신하며, 남은 시간에 따라 이후 뷰를 건너뜁니다.
- 각 뷰 실행 성공 여부와 요약 건수를 로그에 남깁니다.
- 별도의 테스트 함수
DASH_testRefreshAllViews()로 결과를 간단히 확인할 수 있게 합니다.
2) 붙여넣을 위치
- 동일 프로젝트
Code.gs하단, 기존 대시보드 관련 함수들 아래에 붙입니다.
3) 붙여넣은 뒤 할 일
- 편집기에서
DASH_refreshAllViews를 한 번 실행해 권한을 허용합니다. - 그 다음
DASH_testRefreshAllViews를 실행해 결과 로그를 확인합니다.
function DASH_refreshAllViews() { // → 세 뷰를 한 번에 갱신
const lock = LockService.getScriptLock(); // → 스크립트 잠금 객체
const lockWaitMs = 5 * 1000; // → 최대 5초까지 대기
if (!lock.tryLock(lockWaitMs)) { // → 잠금 획득 시도
Logger.log('[DASH_refreshAllViews] 다른 실행이 진행 중입니다. 이번 실행을 건너뜁니다.'); // → 중복 방지 로그
return { success: false, reason: 'LOCKED' }; // → 잠금 실패 반환
}
const started = new Date(); // → 시작 시각 기록
const log = []; // → 실행 결과 로그 배열
const results = { // → 각 뷰 결과 요약
daily: { ok: false, count: 0, skipped: false, error: null },
status: { ok: false, count: undefined, skipped: false, error: null },
yard: { ok: false, count: undefined, skipped: false, error: null },
};
try { // → 전체 실행 감싸기
const maxMs = DASH_CONFIG4.MAX_REFRESH_MINUTES * 60 * 1000; // → 허용 총시간(ms)
// 내부 헬퍼: 남은 시간이 충분한지 검사
const hasTime = () => { // → 남은 시간 확인 함수
const now = new Date();
const elapsed = now.getTime() - started.getTime(); // → 경과 시간
return elapsed < maxMs; // → 한도 이내 여부
};
// 1. 일일 예약 현황 갱신
if (hasTime()) { // → 시간 여유 확인
try {
const dailyCount = DASH_updateApptDailyView(); // → 1편 함수 호출 (숫자 반환)
results.daily.ok = true; // → 성공 표시
results.daily.count = Number.isFinite(dailyCount) ? dailyCount : 0; // → 건수 기록
log.push(`Daily view updated: ${results.daily.count}`); // → 결과 로그
} catch (e) {
results.daily.error = e.message || String(e); // → 오류 메시지 저장
log.push(`Daily view FAILED: ${results.daily.error}`); // → 실패 로그
}
} else {
results.daily.skipped = true; // → 시간 부족으로 스킵
log.push('Daily view skipped due to time limit'); // → 스킵 로그
}
// 2. 상태별 예약 현황 갱신
if (hasTime()) { // → 남은 시간 재확인
try {
const statusResult = DASH_updateApptStatusView(); // → 2편 함수 호출 (숫자 또는 undefined)
results.status.ok = true; // → 성공 표시
if (Number.isFinite(statusResult)) { // → 숫자 반환 시
results.status.count = statusResult;
log.push(`Status view updated: ${statusResult}`); // → 건수 로그
} else {
log.push('Status view updated'); // → 성공 로그
}
} catch (e) {
results.status.error = e.message || String(e); // → 오류 메시지 저장
log.push(`Status view FAILED: ${results.status.error}`); // → 실패 로그
}
} else {
results.status.skipped = true; // → 시간 부족으로 스킵
log.push('Status view skipped due to time limit'); // → 스킵 로그
}
// 3. 야드 트레일러 현황 갱신
if (hasTime()) { // → 다시 남은 시간 확인
try {
const yardResult = DASH_updateYardStatusView(); // → 3편 함수 호출 (숫자 또는 undefined)
results.yard.ok = true; // → 성공 표시
if (Number.isFinite(yardResult)) { // → 숫자 반환 시
results.yard.count = yardResult;
log.push(`Yard view updated: ${yardResult}`); // → 건수 로그
} else {
log.push('Yard view updated'); // → 성공 로그
}
} catch (e) {
results.yard.error = e.message || String(e); // → 오류 메시지 저장
log.push(`Yard view FAILED: ${results.yard.error}`); // → 실패 로그
}
} else {
results.yard.skipped = true; // → 시간 부족으로 스킵
log.push('Yard view skipped due to time limit'); // → 스킵 로그
}
} finally { // → 끝나면 항상 실행
const ended = new Date(); // → 종료 시각
const elapsedMs = ended.getTime() - started.getTime(); // → 경과 시간(ms)
const elapsedMin = (elapsedMs / 1000 / 60).toFixed(2); // → 분 단위 변환
log.push(`Elapsed minutes: ${elapsedMin}`); // → 시간 로그
if (elapsedMs > DASH_CONFIG4.MAX_REFRESH_MINUTES * 60 * 1000) { // → 한도 초과
log.push(
`WARNING: refresh exceeded ${DASH_CONFIG4.MAX_REFRESH_MINUTES} minutes`, // → 경고
);
}
Logger.log('[DASH_refreshAllViews] ' + log.join(' | ')); // → 전체 로그 출력
lock.releaseLock(); // → 잠금 해제
}
const anyOk = results.daily.ok || results.status.ok || results.yard.ok; // → 일부 성공 여부
return { // → 요약 결과 반환
success: anyOk, // → 하나라도 성공했는지
results, // → 상세 결과
};
}
// DASH_refreshAllViews 동작 확인용 테스트 함수
function DASH_testRefreshAllViews() {
const result = DASH_refreshAllViews(); // → 전체 갱신 실행
Logger.log(JSON.stringify(result)); // → 결과를 JSON으로 로그 출력
}제대로 됐는지 확인하는 법: 편집기에서 DASH_refreshAllViews 를 선택해 실행 후, 실행 로그에 Daily view updated·Status view updated·Yard view updated 또는 ... skipped due to time limit 메시지가 보이고, 오류가 없다면 정상입니다.
DASH_testRefreshAllViews 를 실행하면 {"success":true/false,"results":{...}} 형식의 JSON이 로그에 찍힙니다. 이 값을 보고 각 뷰가 실제로 호출됐는지, 어느 부분이 스킵되거나 실패했는지를 빠르게 확인할 수 있습니다.
실행 시간이 길어지는 편이라면 DASH_CONFIG4.MAX_REFRESH_MINUTES 값을 3~4분 정도로 줄여 두고, “어디까지는 반드시 돌리고 어디부터는 다음 회차에 맡길지”를 조정하는 것도 가능합니다.
화면용 숫자 모으기: TODAY 시트에서 요약 값 읽는 함수
구글시트 물류 대시보드 만들기에서 실제 화면에는 긴 표 대신 요약 숫자 몇 개가 더 유용할 때가 많습니다. 이미 TODAY 시트에 필요한 데이터가 계산되어 있다면, 이를 가볍게 훑어 화면에 필요한 최소한의 값만 모아 주는 함수가 좋습니다.
여기서 예시로 구현하는 DASH_getDashboardData() 는 다음 네 가지를 객체로 돌려줍니다.
- 오늘 날짜 문자열(
date) - TODAY 시트에서 A열에 값이 있는 행 수(예약 건수로 가정)
- J열에
'YARD'가 표시된 행 수(야드에 있는 트레일러 수 예시) - 함수 실행 시각 문자열(
refreshedAt)
TODAY 시트 구성이 현장마다 다르기 때문에, 이 함수는 “예시 구조”입니다. 특히 J열은 앞 편에서 만든 야드 현황 블록과 정확히 일치하지 않을 수 있으므로, 실사용 전에는 반드시 본인 시트의 열 배치를 확인한 뒤 집계 기준을 수정해야 합니다.
또한, 이 함수가 올바른 형식으로 값을 돌려주는지 확인하기 위해 테스트 함수에서 오늘 날짜와 일치하는지, 숫자 필드가 실제 숫자인지 검증하는 로직도 함께 둡니다.
1) 이 코드가 하는 일
- TODAY 시트를 훑어 예약 수와 야드 트레일러 수를 세고, 현재 시각과 함께 객체로 반환합니다.
- 테스트 함수에서
date필드가 오늘 날짜인지,totalAppts가 숫자인지 검증한 후 PASS/FAIL 로그를 남깁니다.
2) 붙여넣을 위치
DASH_refreshAllViews와DASH_testRefreshAllViews아래에 그대로 붙입니다.
3) 붙여넣은 뒤 할 일
DASH_testGetDashboardData를 실행해 실행 로그에서 결과와 PASS 여부를 확인합니다.
function DASH_getDashboardData() { // → 화면용 요약 데이터 생성
const ss = SpreadsheetApp.getActiveSpreadsheet(); // → 현재 스프레드시트
const sheet =
ss.getSheetByName(DASH_CONFIG4.TODAY_SHEET_NAME); // → TODAY 시트 찾기
if (!sheet) { // → 시트 없으면
throw new Error('TODAY 시트를 찾을 수 없습니다.'); // → 오류 던지기
}
const lastRow = sheet.getLastRow(); // → 마지막 행 번호
let totalAppts = 0; // → 예약 수 합계
let yardCount = 0; // → 야드 트레일러 수 예시
// 범위는 최대 10열까지만 안전하게 읽도록 제한
const maxCol = Math.min(10, sheet.getLastColumn());
if (lastRow > 1) { // → 데이터가 있을 때만
const range = sheet.getRange(2, 1, lastRow - 1, maxCol); // → A2 ~ 최대 열 범위
const values = range.getValues(); // → 값 읽기
values.forEach((row) => { // → 각 행 반복
// A열에 값이 있으면 예약 1건으로 간주
if (row[0]) {
totalAppts += 1;
}
// J열(10번째 열)에 'YARD' 표기가 있고, 실제로 해당 열까지 존재할 때만 집계
if (row.length > 9 && row[9] === 'YARD') {
yardCount += 1;
}
});
}
const now = new Date(); // → 현재 시각
const tz = DASH_CONFIG4.TIMEZONE; // → 시간대
const dateStr = Utilities.formatDate(now, tz, 'yyyy-MM-dd'); // → 날짜 문자열
const timeStr = Utilities.formatDate(now, tz, 'HH:mm:ss'); // → 시각 문자열
return { // → 요약 객체 반환
date: dateStr, // → 오늘 날짜
totalAppts: totalAppts, // → 예약 수
yardTrailers: yardCount, // → 야드 트레일러 수
refreshedAt: `${dateStr} ${timeStr}`, // → 갱신 시각
};
}
// DASH_getDashboardData 동작 및 기대값 검증용 테스트 함수
function DASH_testGetDashboardData() {
const data = DASH_getDashboardData(); // → 요약 데이터 가져오기
const today = Utilities.formatDate(
new Date(),
DASH_CONFIG4.TIMEZONE,
'yyyy-MM-dd',
);
const isValid =
data.date === today && typeof data.totalAppts === 'number';
// PASS 여부와 함께 전체 객체를 JSON으로 출력
Logger.log(
'[PASS:' + (isValid ? 'true' : 'false') + ']' + JSON.stringify(data),
);
}제대로 됐는지 확인하는 법: DASH_testGetDashboardData 를 실행한 뒤 실행 로그에
"[PASS:true]{...}"와 같이 PASS:true 로 시작하고- 이어서
{"date":"2026-08-25","totalAppts":10,"yardTrailers":3,"refreshedAt":"..."}
형식의 JSON이 보이면 정상입니다.
주의할 점은 row.length > 9 && row[9] === 'YARD' 조건입니다.
- TODAY 시트의 실제 열 수가 10열 미만인 경우 J열을 접근하지 않고 넘어가도록 방어
- J열에
Y,yard, 빈 값 등 다른 표기를 쓰고 있다면 이 함수에서는 야드로 집계되지 않습니다.
실제 표기와 맞추거나, 기준 문자열을 상수로 빼서 관리하는 것을 권장합니다.
ScriptApp 트리거 중복 방지: 항상 트리거 1개만 유지하기
이제 DASH_refreshAllViews 를 일정 주기로 자동 실행하는 Apps Script 시간 트리거를 설정하겠습니다. 실무에서 자주 발생하는 문제는 다음 두 가지입니다.
- 같은 함수에 시간 트리거를 여러 번 걸어 중복 실행되는 경우
- 테스트·수정 과정에서 예전 트리거를 잊어버려, 서로 다른 주기로 겹쳐 도는 경우
이를 피하려면 “설정 함수 하나로 해당 함수의 트리거를 모두 지우고, 원하는 주기로 1개만 다시 만든다”는 원칙으로 관리하는 것이 좋습니다. 아래 DASH_resetRefreshTrigger() 가 그 역할을 합니다.
1) 이 코드가 하는 일
- 현재 프로젝트의 트리거 목록에서
DASH_refreshAllViews를 핸들러로 가진 트리거를 모두 삭제하고,everyMinutes(5)기준으로 새 시간 트리거를 하나 생성합니다.
2) 붙여넣을 위치
DASH_getDashboardData와DASH_testGetDashboardData아래, 같은 파일에 붙입니다.
3) 붙여넣은 뒤 할 일
DASH_resetRefreshTrigger를 한 번 실행해 권한을 허용하고, 트리거 목록에서 실제 생성 여부를 확인합니다.
function DASH_resetRefreshTrigger() { // → 시간 트리거 재설정
const funcName = DASH_CONFIG4.TRIGGER_FUNCTION_NAME; // → 함수 이름 상수
const triggers = ScriptApp.getProjectTriggers(); // → 현재 프로젝트 트리거
// 1. 기존 동일 함수 트리거 삭제
triggers.forEach((t) => { // → 각 트리거 반복
if (t.getHandlerFunction() === funcName) { // → 대상 함수인지 확인
ScriptApp.deleteTrigger(t); // → 해당 트리거 삭제
}
});
// 2. 새 시간 트리거 생성
ScriptApp.newTrigger(funcName) // → 새 트리거 생성
.timeBased() // → 시간 기반 트리거
.everyMinutes(5) // → 5분마다 실행 예시
.create(); // → 트리거 만들기
Logger.log(
`Time trigger for ${funcName} has been reset.`, // → 로그 출력
);
}제대로 됐는지 확인하는 법: DASH_resetRefreshTrigger 실행 후 Apps Script 편집기 상단 메뉴에서 “트리거”를 열어 보면, DASH_refreshAllViews 를 대상으로 한 시간 트리거가 정확히 하나만 존재하면 정상입니다.
실행 간격은 운영 환경에 맞게 바꾸시면 됩니다. 예를 들어 10분 주기로만 봐도 충분하다면 everyMinutes(10) 으로, 하루에 몇 번만 갱신하면 된다면 everyHours(1) 형태로 조정할 수 있습니다. 다만 한 번의 실행에서 세 뷰 전체를 갱신해야 하므로, DASH_CONFIG4.MAX_REFRESH_MINUTES 보다 충분히 큰 간격을 두는 것이 좋습니다.
메뉴 버튼 추가: 수동으로도 한 번에 새로 고침할 수 있게 하기
자동화가 있어도 수동으로 “지금 한 번만 더 갱신”하고 싶은 순간이 있습니다. 그때 각각의 뷰 버튼을 따로 누르는 대신, 메뉴에 “대시보드 전체 새로 고침” 항목 하나만 두면 운영자 입장에서 훨씬 간단합니다.
이 시리즈에서는 공용 onOpen() 안에서 “창고 도구”(또는 예약 도구) 메뉴를 만들고, 각 편의 DASH_addDashboardMenu_ 함수에서 항목을 추가하는 구조를 사용해 왔습니다. 이번 편에서도 그 구조를 그대로 이어갑니다.
1) 이 코드가 하는 일
- 기존 대시보드 메뉴에 “대시보드 전체 새로 고침” 항목을 추가하고, 클릭 시
DASH_refreshAllViews를 실행합니다.
2) 붙여넣을 위치
- 이미 있는
DASH_addDashboardMenu_함수 내부, 다른 메뉴 항목들 아래에 한 줄 추가합니다. (이 함수 자체 코드는 앞 편에 있으므로 다시 싣지 않습니다.)
3) 붙여넣은 뒤 할 일
- 저장 후 스프레드시트를 새로 열어 메뉴를 확인합니다.
// 위치: 이미 있는 DASH_addDashboardMenu_(menu) 함수 안, 다른 메뉴들 아래에 한 줄 추가
menu.addItem('대시보드 전체 새로 고침', 'DASH_refreshAllViews'); // → 전체 갱신 버튼제대로 됐는지 확인하는 법: 시트를 다시 열었을 때 상단 메뉴에 “대시보드 전체 새로 고침” 항목이 보이고, 클릭 시 TODAY 시트의 세 영역(일일·상태·야드)이 함께 갱신되면 성공입니다.
이 구조를 사용하면 앞으로 뷰를 하나 더 추가하더라도, DASH_refreshAllViews 안에서만 호출 순서를 늘려 주면 메뉴·트리거 설정은 그대로 재사용할 수 있습니다.
실무 팁: 트리거·집계 기준을 정리할 때 체크할 것들
마지막으로, 실제 물류 운영에서 구글시트 대시보드 자동화를 돌리면서 겪었던 부분 중 이번 주제와 직접 연결되는 것만 정리합니다.
먼저, ScriptApp 트리거 중복 방지는 “원칙”으로 가져가야 합니다. 같은 핸들러 함수에 시간 트리거가 두 개만 달려도, TODAY 시트를 동시에 건드리면서 예상하지 못한 상태가 생깁니다. 특히 야드 현황은 실시간성이 중요한데, 두 실행이 교차하면서 색상·표기 상태가 꼬이면 현장에서는 시스템을 신뢰하지 않게 됩니다. 가능하면 이 글처럼 DASH_resetRefreshTrigger 같은 전용 함수로 “트리거를 재설정하는 통로는 하나만 둔다”는 식으로 관리하는 편이 안정적입니다.
둘째, 요약 집계는 “어떤 열을 기준으로 삼는지”를 명확하게 문서화해야 합니다. 이 글의 DASH_getDashboardData() 처럼 TODAY 시트 A열에 값이 있으면 예약으로 세고, J열이 'YARD' 인 경우에만 야드 트레일러로 집계하는 규칙을 정했다면, 나중에 TODAY 시트 구조를 바꿀 때 가장 먼저 이 함수를 수정해야 합니다. 그렇지 않으면 화면 숫자와 실제 표 내용이 어긋나 운영자가 혼란을 겪습니다.
셋째, 6분 한도는 “평균 실행 시간”이 아니라 “최악의 날”을 기준으로 잡는 것이 좋습니다. 평소에는 1~2분에 끝나더라도, 피크 시즌이나 예외적으로 예약이 많이 몰리는 날에는 같은 코드가 6분을 넘길 수 있습니다. MAX_REFRESH_MINUTES 를 보수적으로 두고, 남은 시간이 부족하면 일부 뷰를 다음 회차로 넘기는 구조를 미리 설계해 두면, 바로 그날 한도를 넘겨 전체 갱신이 중단되는 상황을 줄일 수 있습니다.
넷째, 이 글에서 전제로 사용하는 함수 반환 규칙—DASH_updateApptDailyView 는 숫자, DASH_updateApptStatusView 와 DASH_updateYardStatusView 는 숫자 또는 undefined 를 반환—은 반드시 실제 구현과 맞춰야 합니다. 만약 앞 편 코드에서 반환형을 다르게 구현했다면, 이 글의 부분을 그대로 쓰되 해당 함수들이 위 규칙을 만족하도록 먼저 정리하는 것을 권장합니다. 그래야 DASH_refreshAllViews 안에서 건수를 안전하게 로그로 남길 수 있습니다.
마지막으로, 이 시스템은 회사 전체 WMS 를 대체하지 않고, 예약·도착·야드 상태 사이의 빈틈을 채우기 위한 보완 도구입니다. 그래서 코드도 “현장에서 당장 필요한 정보와 흐름”에 맞춰 설계했습니다. 독자분들도 그대로 복제하기보다는, 자기 창고 TODAY 시트의 구조와 지표에 맞게 DASH_getDashboardData() 집계 기준이나 트리거 간격을 조정하는 것이 좋습니다.
맺음말: 우선 DASH_refreshAllViews 와 트리거부터 붙여 보시기 바랍니다
세 뷰를 따로 새로 고치는 구조에서 하나의 함수와 하나의 시간 트리거로 정리하면, 운영자가 신경 쓸 포인트가 크게 줄어듭니다. 동시에 ScriptApp 트리거 중복과 6분 실행 한도라는 실무 문제도 같이 정리할 수 있습니다.
지금 할 수 있는 가장 간단한 액션은, 이 글의 DASH_refreshAllViews, DASH_testRefreshAllViews, DASH_getDashboardData, DASH_testGetDashboardData, DASH_resetRefreshTrigger 코드를 프로젝트에 붙여 넣고, DASH_resetRefreshTrigger 를 실행해 5~10분 간격 정도의 시간 트리거를 한 번만 거는 것입니다.
다음 날 아침 TODAY 시트를 열었을 때, 아무도 새로 고침을 누르지 않았는데도 일일·상태·야드 뷰가 모두 최신 상태로 떠 있고, DASH_testGetDashboardData 로그에 PASS:true 가 찍혀 있다면, 구글시트 대시보드 자동 갱신이 제대로 자리를 잡았다는 의미입니다.