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

재배포할 때마다 주소가 바뀌면 안 된다: /exec URL 고정

clasp으로 새 버전을 올릴 때 deployment ID를 재사용하면 /exec URL이 고정된다. 공유 시트 쓰기는 LockService로 막는다.

재배포는 deployment ID를 재사용해야 /exec URL이 고정된다. 새 deployment를 만들 때마다 웹앱 주소가 바뀌면, 고객이 저장한 링크와 외부 웹훅 연동이 그날로 깨진다.

# 나쁨: 매번 새 deployment -> 새 URL
clasp deploy

# 좋음: 같은 deployment ID 재사용 -> URL 고정
clasp deploy -i <deploymentId> -d "v12 $(git rev-parse --short HEAD)"

왜 중요한가

배포 URL은 계약된 인터페이스다. 고객 북마크, QR 코드, 다른 시스템의 웹훅이 그 주소를 가리킨다. 재배포마다 주소가 바뀌면 매번 모든 연동을 다시 알려야 하고, 놓치면 “어제까지 되던 게 안 된다”는 운영 사고가 된다.

push와 deploy는 다르다

  • clasp push는 현재 코드를 Apps Script 프로젝트의 HEAD에 덮어쓴다. 이건 “저장”이지 “배포”가 아니다.
  • 사용자는 HEAD가 아니라 버전이 고정된 deployment를 실행한다.
  • 그래서 개발 검증은 항상 최신 코드를 보는 /dev URL로, 납품·운영 스모크는 버전 고정된 /exec URL로 한다.

clasp push는 원격 전체를 덮어쓴다. 누군가 웹 편집기에서 고친 파일이 있으면 사라질 수 있으니, 재배포 전에 원격 변경을 확인한다.

같은 자산을 동시에 쓰면 LockService

여러 사용자나 트리거가 같은 시트에 동시에 쓰면, 번호가 겹치거나 행이 덮어써진다. 읽기-수정-쓰기가 붙은 작업(append, 번호 발급, 상태 변경)은 lock으로 감싼다.

function appendWithLock(row) {
  const lock = LockService.getScriptLock();
  if (!lock.tryLock(10000)) throw new Error("lock 획득 실패");
  try {
    const sheet = SpreadsheetApp.getActive().getSheetByName("AuditLog");
    sheet.appendRow(row);
    SpreadsheetApp.flush(); // 해제 전에 반드시 반영
  } finally {
    lock.releaseLock(); // 항상 finally에서
  }
}

규칙: tryLock(timeout)을 우선 쓰고, releaseLock()은 항상 finally에 둔다. lock을 풀기 전에 SpreadsheetApp.flush()로 쓰기를 확정해야 다음 실행이 옛 값을 읽지 않는다. lock 안에서는 네트워크 호출이나 긴 계산을 하지 않는다.

깊이: 동시 실행 천장

같은 사용자 기준 동시 실행은 약 30개가 천장이다. LockService 대기까지 고려하면 무손실 동시 사용자는 대략 60명 선으로 설계한다. 그 이상이면 외부 DB나 큐를 검토한다.

핵심 한 줄: 주소는 deployment ID로 고정하고, 공유 쓰기는 lock+flush로 지켜라.

자주 묻는 질문

clasp deploy를 실행할 때마다 웹앱 URL이 바뀌는 이유는?
명령어를 옵션 없이 실행하면 매번 새 deployment가 생성되어 새 URL이 발급됩니다. -i 플래그로 기존 deployment ID를 지정해 재사용하면 /exec URL이 고정됩니다.
clasp push와 clasp deploy는 어떻게 다른가요?
clasp push는 코드를 Apps Script 프로젝트의 HEAD에 덮어쓰는 저장 동작입니다. 사용자가 실제 실행하는 것은 버전이 고정된 deployment이므로, 운영 반영은 반드시 clasp deploy도 함께 해야 합니다.
여러 사용자가 같은 시트에 동시에 쓸 때 LockService를 어떻게 써야 하나요?
tryLock(timeout)으로 잠금을 획득하고, 쓰기 후 SpreadsheetApp.flush()로 반영을 확정한 뒤, finally 블록에서 반드시 releaseLock()을 호출합니다. lock 안에서 네트워크 호출은 피해야 합니다.