Haeminway haeminway
English
기술 노트로
2 분 분량

동시에 같은 시트를 쓰면 깨진다: 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나 큐로 전환을 검토해야 합니다.