- 2026.07.29 Apps Script 프로젝트가 늘어나면 반드시 망가지는 것들
- 2026.07.16 매일 돌던 Apps Script 트리거가 갑자기 멈추는 이유
- 2026.07.15 브라우저 저장소에 고객정보 넣으면 생기는 문제
- 2026.07.07 브라우저가 내 데이터를 버릴 때: 초록 체크가 거짓말을 한다
- 2026.07.07 설치됨 ≠ 작동함: 백업 트리거가 한 번도 안 돌았다
- 2026.07.07 GAS엔 점진 배포가 없다: '맞는' 검사 하나가 전 사용자를 잠근다
- 2026.07.07 시트가 텍스트를 날짜로 바꾼다: '2026-06'이 완료를 영영 막을 때
- 2026.06.18 여러 사람이 한 시트에 쓰는 점검 앱: 동시성·중복·내보내기 설계
- 2026.06.12 셀을 한 칸씩 읽지 마라: 서비스 호출이 진짜 비용이다
- 2026.06.11 시트 셀에 JSON·이미지를 통째로 넣으면 터진다
- 2026.06.10 웹앱 배포 설정 하나가 보안 모델을 정한다: Execute as / 접근 권한
- 2026.06.09 GAS 6분 실행 한도: 멈추기 전에 끊어서 재개하라
- 2026.06.08 PropertiesService는 데이터베이스가 아니다: 9KB의 벽 지금 보는 글
- 2026.06.07 모바일에서 화면을 이미지로 저장할 때 멈추는 진짜 이유
- 2026.06.06 동시에 같은 시트를 쓰면 깨진다: LockService와 30 동시 실행 천장
- 2026.06.05 재배포할 때마다 주소가 바뀌면 안 된다: /exec URL 고정
- 2026.06.04 외부 호출은 가끔 실패한다: 지수 백오프와 재시도 예산
- 2026.06.03 HtmlService 앱은 껍데기 먼저: 비동기 로드와 모바일 뒤로가기
- 2026.06.02 GAS를 졸업해야 할 때: 한계 신호와 외부 DB 이전
- 2026.06.01 GAS에 지능과 엔진룸을 더하기: Vertex AI와 Cloud Run
PropertiesService는 데이터베이스가 아니다: 9KB의 벽
Properties 값은 한 개당 약 9KB가 한계다. 캐시는 유실돼도 되는 데이터에만. 큰 상태는 Sheet·Drive·외부 DB로. JSON 직렬화는 타입을 잃는다.
PropertiesService는 작은 저장소이지 데이터베이스가 아니다. 값 하나당 약 9KB, store 전체도 한계가 있다. 커서·작은 설정·idempotency key 정도만 두고, 그 이상은 다른 저장소로 보낸다.
왜 중요한가
“상태를 Properties에 JSON으로 저장”으로 시작하면, 데이터가 커진 어느 날 9KB에서 조용히 실패한다. 게다가 캐시에만 둔 상태는 TTL이 지나면 사라진다. 유실되면 안 되는 데이터를 휘발성 저장소에 두는 것이 사고의 시작이다.
저장소 선택 규칙
| 저장소 | 용도 | 한계 |
|---|---|---|
| CacheService | 유실돼도 되는 가속용 캐시 | 키당 100KB, TTL 기반 |
| PropertiesService | 커서·설정·플래그 | 값 ~9KB |
| Sheet | 사용자에게 보이는 표 형태 상태 | 셀 5만 자 |
| Drive JSON | 큰 불투명 상태(파일 관리 필요) | 파일 크기 |
| 외부 DB | 다수 사용자·쿼리 많은·대량 | — |
핵심: 데이터를 잃어도 되는가? 그 답이 CacheService와 나머지를 가른다.
JSON 직렬화는 타입을 잃는다
JSON.stringify()/parse()만으로는 Date·Map·Set·undefined·NaN이 안전하게 복원되지 않는다.
// Date를 ISO 문자열로 저장, 읽을 때 복원
const replacer = (k, v) => (v instanceof Date ? { __d: v.toISOString() } : v);
const reviver = (k, v) => (v && v.__d ? new Date(v.__d) : v);
props.setProperty("job", JSON.stringify(state, replacer));
const state = JSON.parse(props.getProperty("job") || "{}", reviver);
깊이: 100KB 넘는 캐시는 분할
CacheService 키 하나는 100KB 한계다. 더 크면 청크로 쪼개 여러 키에 나눠 담고, 재조립 키를 둔다. 단 캐시는 항상 재생성 경로가 있어야 한다.
핵심 한 줄: 작은 건 Properties, 휘발은 Cache, 큰 건 Sheet·Drive·DB. 타입은 replacer/reviver로 지켜라.
자주 묻는 질문
- PropertiesService에 JSON 상태를 저장하면 왜 문제가 생기나요?
- 값 하나당 약 9KB 한계가 있어서 데이터가 커지면 조용히 실패합니다. 커서·설정·idempotency key 정도만 두고 큰 상태는 Sheet, Drive, 외부 DB로 보내야 합니다.
- CacheService와 PropertiesService는 언제 각각 써야 하나요?
- 유실돼도 되는 가속용 데이터는 CacheService(키당 100KB, TTL 기반), 유실되면 안 되는 커서나 설정은 PropertiesService(값당 약 9KB)에 둡니다.
- JSON.stringify/parse로 저장하면 어떤 타입이 손실되나요?
- Date, Map, Set, undefined, NaN은 JSON 직렬화만으로 안전하게 복원되지 않습니다. replacer/reviver를 명시해 타입을 보호해야 합니다.
- 2026.07.29 Apps Script 프로젝트가 늘어나면 반드시 망가지는 것들
- 2026.07.16 매일 돌던 Apps Script 트리거가 갑자기 멈추는 이유
- 2026.07.15 브라우저 저장소에 고객정보 넣으면 생기는 문제
- 2026.07.07 브라우저가 내 데이터를 버릴 때: 초록 체크가 거짓말을 한다
- 2026.07.07 설치됨 ≠ 작동함: 백업 트리거가 한 번도 안 돌았다
- 2026.07.07 GAS엔 점진 배포가 없다: '맞는' 검사 하나가 전 사용자를 잠근다
- 2026.07.07 시트가 텍스트를 날짜로 바꾼다: '2026-06'이 완료를 영영 막을 때
- 2026.06.18 여러 사람이 한 시트에 쓰는 점검 앱: 동시성·중복·내보내기 설계
- 2026.06.12 셀을 한 칸씩 읽지 마라: 서비스 호출이 진짜 비용이다
- 2026.06.11 시트 셀에 JSON·이미지를 통째로 넣으면 터진다
- 2026.06.10 웹앱 배포 설정 하나가 보안 모델을 정한다: Execute as / 접근 권한
- 2026.06.09 GAS 6분 실행 한도: 멈추기 전에 끊어서 재개하라
- 2026.06.08 PropertiesService는 데이터베이스가 아니다: 9KB의 벽 지금 보는 글
- 2026.06.07 모바일에서 화면을 이미지로 저장할 때 멈추는 진짜 이유
- 2026.06.06 동시에 같은 시트를 쓰면 깨진다: LockService와 30 동시 실행 천장
- 2026.06.05 재배포할 때마다 주소가 바뀌면 안 된다: /exec URL 고정
- 2026.06.04 외부 호출은 가끔 실패한다: 지수 백오프와 재시도 예산
- 2026.06.03 HtmlService 앱은 껍데기 먼저: 비동기 로드와 모바일 뒤로가기
- 2026.06.02 GAS를 졸업해야 할 때: 한계 신호와 외부 DB 이전
- 2026.06.01 GAS에 지능과 엔진룸을 더하기: Vertex AI와 Cloud Run