동시에 같은 시트를 쓰면 깨진다: LockService와 30 동시 실행 천장
여러 사용자·트리거가 같은 자산을 쓰면 번호가 겹치고 행이 덮인다. tryLock + finally + flush로 막고, 동시 실행 30/user 천장을 설계에 넣어라.
읽기-수정-쓰기가 붙은 작업은 lock으로 감싸라. append, 번호 발급, 상태 변경처럼 “현재 값을 읽고 → 바꿔서 → 쓰는” 작업은, 두 실행이 겹치면 같은 값을 읽어 하나가 덮어쓴다.
왜 중요한가
접수번호가 중복 발급되거나, 동시에 제출한 두 건 중 하나가 사라진다. 평소엔 안 보이다가 사용자가 몰리는 순간 터지므로, 재현이 어렵고 신뢰를 깎는다.
lock 종류와 표준 패턴
| 종류 | 직렬화 범위 |
|---|---|
getScriptLock() | 프로젝트 전체에서 하나의 임계구역 |
getDocumentLock() | 같은 문서 안에서만 (standalone에선 null 가능) |
getUserLock() | 같은 사용자 실행만 |
function issueNumber() {
const lock = LockService.getScriptLock();
if (!lock.tryLock(10000)) throw new Error("lock 획득 실패");
try {
const sheet = SpreadsheetApp.getActive().getSheetByName("Counter");
const next = Number(sheet.getRange("A1").getValue()) + 1;
sheet.getRange("A1").setValue(next);
SpreadsheetApp.flush(); // 해제 전 반드시 반영
return next;
} finally {
lock.releaseLock(); // 항상 finally
}
}
규칙:
tryLock(timeoutMs)을 우선 쓴다(무한 대기 금지).releaseLock()은 항상finally에 둔다.- 해제 전에
SpreadsheetApp.flush()로 쓰기를 확정한다. 안 하면 다음 실행이 옛 값을 읽는다. - lock 안에서는 네트워크 호출·긴 계산·사용자 대기를 하지 않는다.
깊이: 30 동시 실행 천장
같은 사용자 기준 동시 실행은 약 30개가 천장이다. LockService 대기를 고려하면 무손실 동시 사용자는 대략 60명 선으로 설계한다. 그 이상이면 외부 DB나 큐로 넘어갈 신호다. 이 한계는 영업·계약 단계에서 미리 고지하는 게 안전하다.
핵심 한 줄: 공유 쓰기엔 tryLock + finally + flush. 동시 사용자 한계는 미리 설계하라.
자주 묻는 질문
- GAS에서 동시에 같은 시트에 쓰면 왜 데이터가 깨지나요?
- 두 실행이 겹치면 같은 현재 값을 읽어 하나가 다른 하나를 덮어씁니다. 접수번호 중복 발급이나 동시 제출 건 소실이 대표적인 사례입니다.
- LockService를 올바르게 쓰려면 어떤 규칙을 따라야 하나요?
- tryLock(timeoutMs)으로 획득하고, releaseLock()은 반드시 finally에 두며, 해제 전에 SpreadsheetApp.flush()로 쓰기를 확정해야 합니다.
- GAS의 동시 실행 한도는 얼마이며 어떻게 설계해야 하나요?
- 같은 사용자 기준 동시 실행은 약 30개가 천장입니다. LockService 대기를 고려하면 무손실 동시 사용자는 대략 60명 선으로 설계하고, 그 이상이면 외부 DB나 큐로 전환을 검토해야 합니다.