구글시트 설정값 한곳에 모으기: SETTINGS 시트로 관리하는 방법
도입: 코드를 고치지 않고 설정만 바꾸고 싶을 때
구글시트 입고 예약 시스템을 쓰다 보면 “이번 주만 시작 시간을 8시로 당겨 달라”, “야드 슬롯은 당분간 끄자” 같은 요구가 자주 생깁니다. 이때마다 Apps Script 코드를 열어 상수를 고치면, 실수 위험도 크고 운영자가 개발자에게 계속 도움을 요청해야 합니다.
이 글에서는 구글시트 설정값 한곳에 모으기, 즉 SETTINGS 시트를 만들어 모든 설정을 한 시트에서 관리하는 방법을 다룹니다. SETTINGS 시트 예시 표와 함께, Apps Script에서 이 값을 읽어 오는 loadSettings_() 전체 코드, 예/아니오를 통일해서 처리하는 _isSettingEnabled() 함수, 숫자 설정(슬롯 간격) 헬퍼, CacheService로 설정값을 캐싱하는 방법, 그리고 캐시를 강제로 비우는 메뉴(onOpen 공용 구조 포함)까지 한 번에 정리합니다.
앞 글인 구글시트 입고 예약 시스템 시트 구조 만들기 — 기본 1편에서 시트 구조를 만들고, 구글시트 도크 도어 자동 생성 방법: 30개·야드 슬롯 50개 한 번에 만들기에서 도어·야드 기준 데이터를 채워 넣었다고 가정하고 이어집니다. 이번 3편의 목표는 “코드는 그대로 두고 SETTINGS 시트 값만 바꿔도 동작이 달라지는 구조”를 만드는 것입니다. 아래 코드와 표만 옮겨 적으면, 별도 프레임워크 없이 바로 적용할 수 있도록 구성했습니다.
SETTINGS 시트 구조 설계: 키/값/설명 세 칸으로 시작하기
구글시트 설정 시트 만들기는 복잡하게 시작할 필요가 없습니다. 중요한 것은 사람이 보기에도 이해하기 쉽고, 코드에서도 다루기 쉬운 최소 구조를 만드는 일입니다. 실무에서 가장 무난했던 형태는 다음과 같이 세 개의 열로 구성하는 방식입니다.
- A열: KEY (설정 키)
- B열: VALUE (실제 값)
- C열: DESCRIPTION (설명)
예를 들어 SETTINGS 시트에 다음과 같이 입력합니다.
| KEY | VALUE | DESCRIPTION |
|--------------------|------------|--------------------------------------|
| APPT_START_TIME | 09:00 | 예약 시작 시각(24시간제) |
| APPT_END_TIME | 18:00 | 예약 종료 시각(24시간제) |
| APPT_SLOT_MINUTES | 30 | 슬롯 간격(분) |
| APPT_ENABLE_YARD | TRUE | 야드 슬롯 사용 여부 |
| APPT_TIMEZONE | Asia/Seoul | 시스템 기준 시간대 |
| APPT_MAX_APPTS_DAY | 80 | 하루 최대 예약 건수(예시 수치) |
키 이름에는 이 시리즈에서 사용하는 접두어인 APPT_를 붙였습니다. 이렇게 접두어를 통일해 두면, 프로젝트가 커져도 “예약 관련 설정”만 눈으로 골라 보기 쉽습니다. 설명 열은 코드에서 직접 읽지 않지만, 운영자가 시트를 열었을 때 각 값이 무엇을 의미하는지 바로 파악하게 해 줍니다.
이 구조는 숫자, 문자열, 불리언을 한 시트에 섞어서 관리할 수 있습니다. 숫자(간격, 개수 등)는 나중에 Number()나 parseInt()로 변환해서 쓰고, 예/아니오 값은 _isSettingEnabled()로 통일해서 처리하면 됩니다. 아래에서 이 함수들을 하나씩 실제 코드로 구현합니다.
공용 onOpen 구조: 여러 글 코드를 한 프로젝트에 합칠 때 충돌 없이 쓰는 법
이 시리즈 코드를 한 스크립트 프로젝트 안에 다 넣으면, onOpen 최상위 함수 이름이 중복되어 충돌할 수 있습니다. 구글 Apps Script에서는 같은 이름의 최상위 함수가 두 번 선언되면 마지막 것이 앞의 것을 덮어쓰기 때문에, 앞 편에서 만든 메뉴가 사라지는 문제가 생깁니다.
이를 피하려면 onOpen()은 항상 한 번만 정의하고, 각 글에서는 add○○Menu_(menu)처럼 도우미 함수만 추가하는 공용 구조를 씁니다.
- 프로젝트에 이미
onOpen()이 있다면, 새로운onOpen()코드는 만들지 말고, 아래처럼 도우미만 추가해 기존onOpen()안에서 호출합니다. - 아직
onOpen()이 없다면, 이번 글의 공용 버전을 한 번만 만들고, 이후 다른 글에서도 같은 패턴(addDoorsMenu_(menu),addSettingsMenu_(menu)등)으로 메뉴를 붙이면 됩니다.
공용 onOpen()의 실제 코드는 아래 “캐시 강제 비우기” 절에서 전체를 보여 드립니다.
loadSettings_ 전체 구현: SETTINGS 시트 읽기 + 기본값 + 캐시
이제 본론인 Apps Script loadSettings 사용법입니다. loadSettings_() 함수는 SETTINGS 시트를 읽어 자바스크립트 객체로 바꾸고, CacheService에 잠시 보관했다가 다음 호출에서 재사용하는 역할을 합니다.
아래 코드는 다음을 모두 포함합니다.
- SETTINGS 시트에서 KEY/VALUE/설명 읽기
- 키를 대문자로 통일해 객체로 만들기
- 미리 선언해 둔 기본값과 병합하기
CacheService로 일정 시간 캐싱하기- 오류 시 기본값으로 안전하게 되돌아가기
이 코드는 구글시트 → 확장 프로그램 → Apps Script → Code.gs에 붙여 넣습니다. 위치는 다른 함수들과 상관없지만, 보통 설정 관련 상수와 함께 파일 상단이나 하단에 모아 두면 관리가 쉽습니다. 붙여 넣은 뒤에는 저장(⌘S 또는 Ctrl+S)만 하면 됩니다. 테스트는 뒤에서 안내하는 testLoadSettings_() 함수로 합니다.
먼저, “여기만 본인 것으로 바꾸는 상수들”과 loadSettings_() 본체입니다.
const APPT_SETTINGS_CONFIG = { // → 예약 설정 전용 상수 모음
SETTINGS_SHEET_NAME: 'SETTINGS', // → SETTINGS 시트 이름
CACHE_KEY: 'APPT_SETTINGS_CACHE_V1', // → 캐시 키(버전 포함)
CACHE_SECONDS: 300, // → 캐시 유지 시간(초 단위, 예: 5분)
DEFAULTS: { // → 누락 시 사용할 기본값
APPT_START_TIME: '09:00', // → 기본 예약 시작 시각
APPT_END_TIME: '18:00', // → 기본 예약 종료 시각
APPT_SLOT_MINUTES: '30', // → 기본 슬롯 간격(분, 문자열로 보관)
APPT_ENABLE_YARD: 'TRUE', // → 기본 야드 사용 여부
APPT_TIMEZONE: 'Asia/Seoul', // → 기본 시간대
APPT_MAX_APPTS_DAY: '80', // → 기본 하루 최대 예약(예시)
},
}; //
function loadSettings_() { // → SETTINGS 시트를 읽어 객체로 반환
const cache = CacheService.getScriptCache(); // → 스크립트 캐시 가져오기
const cached = cache.get(APPT_SETTINGS_CONFIG.CACHE_KEY); // → 캐시된 값 읽기
if (cached) { // → 캐시가 있으면
try { // → JSON 파싱 시도
return JSON.parse(cached); // → 캐시된 설정 객체 반환
} catch (e) { // → 파싱 실패 시
Logger.log('설정 캐시 파싱 오류: ' + e); // → 오류 로그 남기기
cache.remove(APPT_SETTINGS_CONFIG.CACHE_KEY); // → 문제 있는 캐시 제거
} //
} //
let settings = {}; // → 읽어 올 설정 객체 준비
try { // → 시트 읽기 전체를 보호
const ss = SpreadsheetApp.getActive(); // → 현재 스프레드시트
const sheet = ss.getSheetByName( // → SETTINGS 시트 찾기
APPT_SETTINGS_CONFIG.SETTINGS_SHEET_NAME
); //
if (!sheet) { // → 시트를 못 찾으면
Logger.log('SETTINGS 시트를 찾지 못했습니다. 기본값을 사용합니다.'); // → 로그 남기기
settings = Object.assign({}, APPT_SETTINGS_CONFIG.DEFAULTS); // → 기본값 복사
} else { // → 시트를 찾은 경우
const lastRow = sheet.getLastRow(); // → 마지막 행 번호
if (lastRow >= 2) { // → 헤더 제외 데이터 존재 여부
const range = sheet.getRange(2, 1, lastRow - 1, 3); // → A2:C마지막 행 범위
const values = range.getValues(); // → 2차원 배열로 값 읽기
values.forEach((row, idx) => { // → 각 행을 순회
const rawKey = String(row[0] || '').trim(); // → A열 키 값 정리
const rawValue = String(row[1] || '').trim(); // → B열 값 정리
if (!rawKey) { // → 키가 비어 있으면
if (rawValue) { // → 값만 있다면
Logger.log('키 없이 값만 있는 행: ' + (idx + 2)); // → 경고 로그
} //
return; // → 이 행은 건너뛰기
} //
const key = rawKey.toUpperCase(); // → 키를 대문자로 통일
settings[key] = rawValue; // → 객체에 설정값 저장
}); //
} else { // → 데이터 행이 없는 경우
Logger.log('SETTINGS 시트에 데이터가 없습니다. 기본값을 사용합니다.'); // → 로그
} //
settings = Object.assign( // → 시트 값과 기본값 병합
{}, // → 새 객체에
APPT_SETTINGS_CONFIG.DEFAULTS, // → 기본값 먼저 복사
settings // → 시트에서 읽은 값으로 덮어쓰기
); //
} //
} catch (e) { // → 전체 읽기 중 예외 발생 시
Logger.log('설정 로딩 중 오류: ' + e); // → 오류 로그 남기기
settings = Object.assign( // → 안전하게 기본값 반환
{}, //
APPT_SETTINGS_CONFIG.DEFAULTS //
); //
} //
try { // → 캐시에 설정 저장 시도
cache.put( // → 캐시 쓰기
APPT_SETTINGS_CONFIG.CACHE_KEY, // → 캐시 키
JSON.stringify(settings), // → JSON 문자열로 저장
APPT_SETTINGS_CONFIG.CACHE_SECONDS // → 캐시 유효 시간(초)
); //
} catch (e) { // → 캐시 쓰기 오류 시
Logger.log('설정 캐시 저장 오류: ' + e); // → 로그만 남기고 계속 진행
} //
return settings; // → 최종 설정 객체 반환
} // testLoadSettings_() 함수를 실행했을 때 실행 로그에 설정 객체가 JSON으로 찍히면 정상입니다.
테스트 헬퍼 함수는 다음과 같습니다.
function testLoadSettings_() { // → 설정 로딩 테스트용 함수
const settings = loadSettings_(); // → SETTINGS 읽어오기
Logger.log(JSON.stringify(settings)); // → 전체 설정을 로그로 출력
} // 실행 후 “실행 로그” 창에 { "APPT_START_TIME": "09:00", ... } 형태로 값이 보이면 성공입니다.
예/아니오 통일: _isSettingEnabled()로 불리언 처리하기
SETTINGS 시트를 여러 사람이 만지기 시작하면 “TRUE, true, 예, YES, Y, 1”처럼 켜짐을 표현하는 방식이 제각각이 됩니다. 이를 그대로 문자열 비교로 처리하면, 오타나 표기 차이 때문에 의도와 다르게 기능이 꺼져 있거나 켜지는 문제가 생깁니다. 이를 막기 위해 _isSettingEnabled() 함수 하나를 두고, 여기서 모든 표기를 통일해 불리언으로 바꾸는 것이 안전합니다.
아래 함수는 다음과 같이 동작합니다.
- 앞뒤 공백 제거, 대문자로 변환 후 비교합니다.
TRUE,YES,Y,1,ON,OK,예,네는 모두true로 봅니다.FALSE,NO,N,0,OFF,아니오는 모두false로 봅니다.- 이외의 값(예:
TURE,YESS,maybe)은 로그를 남기고false를 반환합니다.
function _isSettingEnabled(value) { // → 예/아니오 값 통일 처리
const raw = String(value || '').trim(); // → null 방지·공백 제거
if (!raw) { // → 빈 값이면
return false; // → 기본적으로 꺼짐 처리
} //
const v = raw.toUpperCase(); // → 대문자로 통일
const TRUE_SET = [ // → 허용된 켜짐 표기 목록
'TRUE', 'YES', 'Y', '1', 'ON', 'OK', '예', '네' // → 다양한 표현
]; //
const FALSE_SET = [ // → 허용된 꺼짐 표기 목록
'FALSE', 'NO', 'N', '0', 'OFF', '아니오' // → 다양한 표현
]; //
if (TRUE_SET.indexOf(v) !== -1) { // → 켜짐 목록에 있으면
return true; // → true 반환
} //
if (FALSE_SET.indexOf(v) !== -1) { // → 꺼짐 목록에 있으면
return false; // → false 반환
} //
Logger.log('알 수 없는 설정 값(켜짐/꺼짐): ' + raw); // → 허용 밖 값 로그
return false; // → 안전하게 false 처리
} // testIsSettingEnabled_()를 실행해 여러 예시 값이 어떻게 판별되는지 확인합니다.
function testIsSettingEnabled_() { // → 불리언 변환 테스트
const samples = ['TRUE', 'yes', '예', '0', 'off', 'maybe', '', ' y ']; // → 예시 값
samples.forEach((v) => { // → 각 예시 반복
Logger.log(v + ' → ' + _isSettingEnabled(v)); // → 변환 결과 로그 출력
}); //
} // 실행 로그에서 "maybe → false", "예 → true"처럼 값이 어떻게 해석되는지 보이면 정상입니다.
실제 예약 코드에서는 다음과 같이 사용합니다.
const settings = loadSettings_(); // → SETTINGS 읽기
if (_isSettingEnabled(settings.APPT_ENABLE_YARD)) { // → 야드 사용 여부 판단
// 야드 관련 슬롯을 생성합니다. // → 야드 기능 분기
}이렇게 쓰면 SETTINGS 시트에서 예, YES, 1 중 무엇을 써도 동일하게 “켜짐”으로 인식합니다.
숫자 설정 사용 시 안전 가드: NaN·음수·0 처리 패턴
입고 예약 시스템에서는 APPT_SLOT_MINUTES, APPT_MAX_APPTS_DAY처럼 숫자 설정이 곧바로 계산에 들어갑니다. 이 값이 문자열이거나 잘못 입력되면 Number('ABC')가 NaN이 되고, 이를 사용한 모든 계산이 망가집니다. 실무에서 이를 막기 위해, 숫자 설정을 쓸 때는 항상 변환 + 검증 패턴을 같이 씁니다.
이번 글에서는 슬롯 간격(분)에 대해 전용 헬퍼 getApptSlotMinutes_()를 만들어 씁니다. 이 함수는 설정값을 숫자로 변환하고, 유효하지 않을 경우 로그를 남기고 기본값 30분을 사용합니다.
function getApptSlotMinutes_() { // → 슬롯 간격(분) 안전 반환
const settings = loadSettings_(); // → SETTINGS 읽기
const raw = settings.APPT_SLOT_MINUTES || ''; // → 설정 원본 문자열(없을 경우 빈 문자열)
const num = parseInt(raw, 10); // → 10진수 정수로 변환 시도
if (!Number.isFinite(num) || num <= 0) { // → NaN / 무한대 / 0 이하 방지
Logger.log('유효하지 않은 슬롯 간격: ' + raw + ', 기본값 30분 사용'); // → 경고 로그
return 30; // → 안전 기본값(분)
} //
return num; // → 검증 통과한 값 반환
} // 이 함수는 아래와 같이 테스트해 볼 수 있습니다.
function testGetSlotMinutes_() { // → 슬롯 간격 테스트
const m = getApptSlotMinutes_(); // → 슬롯 간격 가져오기
Logger.log('슬롯 간격(분): ' + m); // → 결과 로그 출력
} // SETTINGS시트에APPT_SLOT_MINUTES가40이면40을 그대로 반환합니다.APPT_SLOT_MINUTES를 비워 두거나,-10,abc처럼 잘못 넣으면 로그에 경고를 남기고30을 사용합니다.- 헬퍼 내부에서 기본값을 숫자 30으로 직접 반환하고 있으므로,
APPT_SETTINGS_CONFIG.DEFAULTS에서 이 값을 문자열'30'로 유지하더라도 동작이 꼬이지 않습니다.
APPT_MAX_APPTS_DAY 같은 다른 숫자 설정이 필요하다면 위 패턴을 복사해 getApptMaxPerDay_()처럼 별도 헬퍼를 만들어 쓰면 됩니다.
캐시 강제 비우기: 공용 onOpen + SETTINGS 메뉴로 즉시 반영하기
CacheService를 쓰면 시트 읽기 횟수를 줄일 수 있지만, 운영자가 SETTINGS 값을 바꾸고 “지금 당장 반영됐는지” 확인하고 싶을 때는 캐시가 오히려 답답하게 느껴질 수 있습니다. 이때는 캐시를 강제로 지우는 작은 함수와 메뉴 항목을 하나 만들어 두면 편합니다.
앞에서 언급했듯이, 이 시리즈는 하나의 공용 onOpen()과 여러 개의 메뉴 도우미 함수를 조합하는 구조를 사용합니다. 이미 이전 글(예: 도크 도어 자동 생성 편)에서 onOpen()을 만들어 두었다면, 거기에 addSettingsMenu_(menu) 호출만 한 줄 더해 주면 됩니다. 아직 onOpen()이 없다면, 아래 공용 예시를 그대로 사용하고, 앞으로 추가되는 기능도 모두 add○○Menu_(menu) 형태로 붙여 나가면 충돌 없이 확장할 수 있습니다.
1) 공용 onOpen: 프로젝트에 딱 한 번만 선언
function onOpen() { // → 시트를 열 때 자동 실행
const ui = SpreadsheetApp.getUi(); // → UI 객체 가져오기
const menu = ui.createMenu('창고 도구'); // → 공용 상단 메뉴 생성
// 이 시리즈의 각 편에서 정의한 메뉴 도우미들을 여기서 호출합니다.
// 예: 도크/야드 메뉴 (다른 글에서 정의했다면)
if (typeof addDoorsMenu_ === 'function') { // → 함수가 정의돼 있으면
addDoorsMenu_(menu); // → 도어·야드 관련 메뉴 추가
}
// 이번 편 SETTINGS 메뉴
if (typeof addSettingsMenu_ === 'function') { // → SETTINGS 메뉴 도우미가 있다면
addSettingsMenu_(menu); // → SETTINGS 관리 메뉴 추가
}
menu.addToUi(); // → 메뉴를 시트에 붙이기
} // - 이미 프로젝트에
onOpen()이 있다면, 위 전체를 새로 만들 필요는 없습니다. - 기존
onOpen()의createMenu('창고 도구')부분을 유지하고, - 그 안에 아래 한 블록만 추가하면 됩니다:
if (typeof addSettingsMenu_ === 'function') {
addSettingsMenu_(menu);
}- 이렇게 하면 여러 글에서 코드를 복사해 와도
onOpen은 한 번만 선언되므로 충돌이 나지 않습니다.
2) SETTINGS 전용 메뉴 도우미 + 캐시 삭제 함수
이번 편에서 필요한 도우미 함수와 캐시 삭제 함수 전체 코드는 아래와 같습니다. 이 코드는 프로젝트 어디에 있어도 상관없지만, 보통 설정 관련 코드 근처에 두면 나중에 찾기 쉽습니다.
function addSettingsMenu_(menu) { // → 이 편 SETTINGS용 메뉴 정의
menu.addItem( // → 메뉴 항목 하나 추가
'설정 캐시 비우기', // → 메뉴에 보일 이름
'clearSettingsCache_' // → 실행할 함수 이름
); //
} //
function clearSettingsCache_() { // → SETTINGS 캐시 제거 함수
const cache = CacheService.getScriptCache(); // → 스크립트 캐시 가져오기
cache.remove(APPT_SETTINGS_CONFIG.CACHE_KEY); // → 설정 캐시 키 제거
SpreadsheetApp.getActive() // → 현재 스프레드시트
.toast('설정 캐시를 비웠습니다. 새 설정이 즉시 반영됩니다.', '알림', 5); // → 우측 하단 알림
} // - 시트를 새로 열었을 때 상단 메뉴에 ‘창고 도구 → 설정 캐시 비우기’가 보이고,
- 클릭 시 우측 하단에 “설정 캐시를 비웠습니다. 새 설정이 즉시 반영됩니다.”라는 토스트 알림이 뜨면 정상입니다.
- 캐시를 비우고 나면, 다음에
loadSettings_()가 호출될 때 SETTINGS 시트를 다시 읽어 최신 값으로 캐시를 갱신합니다.
실무 팁: SETTINGS 시트를 안전하게 운영하는 방법
코드와 구조를 갖춘 뒤에는 운영 과정에서의 안전장치가 중요합니다. 현장에서 SETTINGS 시트를 실제로 운영하면서 도움이 되었던 방법을 정리하면 다음과 같습니다.
- 데이터 유효성 검사 활용
SETTINGS 시트에는 데이터 유효성 검사를 적극적으로 사용합니다. 예/아니오 값이 들어가는 B열에는 드롭다운으로 “TRUE/FALSE” 또는 “예/아니오”만 선택하도록 제한하고, 숫자 설정에는 “숫자만 허용” 규칙을 걸어 둡니다. 이렇게 해 두면 _isSettingEnabled()나 getApptSlotMinutes_()에서 예외 상황을 한 번 더 걸러 주지만, 애초에 틀린 입력이 들어갈 가능성을 크게 줄일 수 있습니다.
- 변경 이력 남기기
간단하게는 D열 이후에 “마지막 수정일, 수정자, 변경 이유”를 두고 사람이 직접 적도록 할 수 있습니다. 조금 더 자동화를 원하면, 별도 글인 구글시트 작업 이력 자동 기록 방법 | Apps Script 9편과 같은 방식으로, SETTINGS 시트의 변경을 감지해 로그 시트에 자동 기록하도록 확장할 수 있습니다.
- 테스트/운영 시트 분리
테스트용 SETTINGS 시트와 운영용 SETTINGS 시트를 분리하는 것이 좋습니다. 같은 구조의 SETTINGS_TEST, SETTINGS 두 개를 두고, APPT_SETTINGS_CONFIG.SETTINGS_SHEET_NAME만 환경에 맞게 바꾸면 됩니다. 새 규칙을 시험해 볼 때는 테스트 시트를 바라보게 설정한 뒤, 충분히 검증하고 나서 운영 시트로 전환하면 현장 혼선을 줄일 수 있습니다.
- 시트 보호 및 권한 설계
구글시트의 보호 기능을 활용해 SETTINGS 시트를 전체 잠궈 두고, 일부 셀만 운영 담당자 계정에게 수정 권한을 부여합니다. 모든 사용자가 마음대로 설정을 바꾸도록 두면, 실수로 값이 지워지거나 ABC 같은 문자열이 숫자 칸에 들어가 전체 일정 계산이 깨질 수 있습니다. 반대로 권한을 지나치게 좁히면 운영이 불편해지므로, 각 센터 사정에 맞게 “수정 권한을 가진 사람”을 1~2명 정도로 정해 두는 것이 현실적인 균형이었습니다.
맺음말: SETTINGS 시트 뼈대부터 만들어 두기
이 글에서는 구글시트 SETTINGS 시트 설정 관리를 중심으로, KEY/VALUE/DESCRIPTION 구조의 시트 예시, Apps Script에서 이를 읽어 오는 loadSettings_() 전체 코드, 불리언 표기를 정리하는 _isSettingEnabled(), 슬롯 간격 설정을 안전하게 쓰기 위한 getApptSlotMinutes_() 헬퍼, CacheService로 캐싱하고 메뉴에서 캐시를 비우는 공용 onOpen 구조까지 정리했습니다. 특히 시리즈 다른 글과 한 프로젝트에 코드를 합쳤을 때 충돌하지 않도록, onOpen()을 공용 하나로 두고 addSettingsMenu_(menu) 같은 도우미 함수로 메뉴를 분리하는 구조를 함께 설명했습니다.
목표는 입고 예약 시스템처럼 설정이 자주 바뀌는 환경에서, 코드 수정 없이 시트 값만으로 동작을 조정할 수 있는 구조를 갖추는 것입니다.
지금 바로 할 수 있는 한 가지 행동을 권한다면, 먼저 예약 관련 스프레드시트에 SETTINGS라는 새 시트를 만들고, 이 글의 표 예시처럼 키/값/설명 세 열만이라도 뼈대를 잡아 보시기 바랍니다. 그다음 위에 제시한 APPT_SETTINGS_CONFIG, loadSettings_(), _isSettingEnabled(), getApptSlotMinutes_() 코드와 addSettingsMenu_(menu), clearSettingsCache_()를 붙여 넣고, testLoadSettings_()와 testGetSlotMinutes_()로 로그를 확인해 보시면, 이후 예약 생성·슬롯 생성 코드에 이 설정들을 연결해 가는 작업이 훨씬 수월해집니다. 다음 편에서는 이렇게 준비한 SETTINGS 값을 실제 입고 예약·슬롯 생성 로직에 연결해, 시트 값만 바꿔도 운영 규칙이 바로 바뀌도록 구현하는 과정을 다룰 예정입니다.