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

셀을 한 칸씩 읽지 마라: 서비스 호출이 진짜 비용이다

GAS에서 느린 건 자바스크립트가 아니라 Sheets·Drive 같은 서비스 호출이다. 루프 안 getValue/setValue를 일괄 getValues/setValues로 바꾸면 수십 배 빨라진다.

GAS 성능의 1순위 규칙: 서비스 호출 횟수를 줄여라. 느린 건 메모리 안 자바스크립트 연산이 아니라, getValue()·setValue()·appendRow() 같은 Sheets/Drive/UrlFetch 서비스 호출이다. 루프 안에서 이걸 반복하면 호출마다 왕복 비용이 쌓인다.

왜 중요한가

셀을 한 칸씩 1,000번 읽는 코드와, 한 번에 읽어 메모리에서 처리하는 코드는 체감 속도가 수십 배 차이 난다. 느린 스크립트는 6분 한도에도 더 빨리 부딪힌다. 비용은 “기다림”과 “중간에 끊김” 둘 다다.

정답: 한 번에 읽고, 메모리에서 처리하고, 한 번에 쓴다

// 나쁨: 행마다 두 번씩 서비스 호출
for (let row = 2; row <= lastRow; row++) {
  const v = sheet.getRange(row, 1).getValue();
  sheet.getRange(row, 2).setValue(transform(v));
}

// 좋음: 호출 2번 (읽기 1 + 쓰기 1)
function updateRows() {
  const sheet = SpreadsheetApp.getActive().getSheetByName("Data");
  const values = sheet.getRange(2, 1, sheet.getLastRow() - 1, 2).getValues();
  const next = values.map(([input]) => [input, transform(input)]);
  sheet.getRange(2, 1, next.length, 2).setValues(next);
}

같은 원칙의 다른 적용

  • 외부 HTTP: 서로 독립적인 호출은 UrlFetchApp.fetchAll()로 한 번에. 단 부분 실패를 { ok, status, body, error }로 정규화하고, 응답은 요청 순서로 매핑한다.
  • 자주 읽는 값: 잘 안 바뀌는 외부 응답·마스터 데이터는 CacheService에 TTL을 두고 캐싱한다(키 100KB 한계, 넘으면 분할).
  • 시트 구조 변경: 서식·검증·열너비처럼 구조를 많이 바꾸면 셀 값 API가 아니라 Advanced Sheets API의 batchUpdate로 한 번에 반영한다.

깊이: UI도 같다

google.script.run 호출을 화면에서 자주 반복하면 라이브러리 로딩 비용이 체감된다. UI-heavy 앱은 라이브러리 의존을 줄이고, 데이터는 껍데기를 먼저 그린 뒤 비동기로 한 번에 가져온다.

핵심 한 줄: 루프 밖으로 서비스 호출을 빼라. 빠르기는 거기서 결정된다.

자주 묻는 질문

GAS 스크립트가 느린 진짜 원인은 무엇인가요?
메모리 안 자바스크립트 연산이 아니라 getValue·setValue·appendRow 같은 Sheets/Drive/UrlFetch 서비스 호출이 원인입니다. 루프 안에서 반복하면 호출마다 왕복 비용이 쌓입니다.
getValues/setValues로 바꾸면 얼마나 빨라지나요?
셀을 한 칸씩 1,000번 읽는 방식과 한 번에 읽어 메모리에서 처리하는 방식은 체감 속도가 수십 배 차이 납니다.
독립적인 외부 HTTP 요청을 빠르게 처리하려면 어떻게 해야 하나요?
UrlFetchApp.fetchAll()로 한 번에 묶어 보내고, 부분 실패는 응답 배열을 요청 순서로 매핑해 처리합니다.