구글 시트 셀이 바뀌었는데 onEdit가 안 돈다면, 누가 값을 썼는지 먼저 봐요

구글 시트에서 셀을 직접 고치면 Apps Script가 실행되는데, 다른 스크립트가 같은 셀을 바꿀 때는 조용한가요? 함수 이름이나 권한부터 고치기 전에 값이 바뀐 경로를 확인해요. Google의 `onEdit(e)` 설명은 사용자가 스프레드시트의 셀 값을 바꿀 때를 가리켜요. `Range.setValue()` 같은 스크립트 실행이나 API 요청으로 값이 바뀌는 경우는 편집 트리거를 다시 일으키지 않는다고 명시해요.

이 글은 시트의 값 변화에 이어 다른 작업을 자동으로 하려는 상황을 다뤄요. 예를 들어 사람이 상태 칸을 ‘완료’로 바꾸면 행을 표시하는 스크립트를 생각해 봐요. 같은 상태를 외부 연동이나 다른 Apps Script가 적어 넣었는데 행 표시가 없었다면 먼저 사람이 고친 건지, 프로그램이 적은 건지 나눠야 해요. 실제 계정에서 테스트했다는 뜻은 아니며, 아래 그림은 Google 화면을 베끼지 않은 자체 제작 설명 도해예요.

보이는 값과 편집 이벤트는 같은 것이 아니에요

시트 화면에 ‘완료’가 나타났다는 사실은 결과만 알려 줘요. 누가 어떤 경로로 그 값을 만들었는지는 별도 질문이에요. Google은 단순 트리거 `onEdit(e)`가 사용자가 스프레드시트에서 값을 변경할 때 실행된다고 설명해요. 다른 스크립트가 `setValue()`로 값을 넣으면 화면은 바뀌지만 `onEdit`를 일으키지 않는다는 예시도 공식 문서에 있어요. 따라서 “셀 값이 바뀌었으니 함수가 반드시 돌았어야 한다”는 가정부터 버려야 해요.

우선 한 건의 시각과 셀 주소를 적어요. 그 값이 입력된 통로가 사람의 키보드 편집인지, 수식의 계산 결과인지, 다른 스크립트의 쓰기인지, 외부 API나 연동인지 확인해요. 특히 수식 셀의 표시 결과만 달라졌다면 사용자가 그 셀 값을 직접 편집한 사건과 같게 취급할 근거가 없어요. 수식 계산을 실시간 감시하는 용도로 `onEdit`를 설계하기 전에 실제 이벤트 종류가 요구에 맞는지 검토해야 해요.

사람이 셀을 직접 수정한 경우라면 다음은 작업 대상 범위예요. 기대한 함수가 특정 시트나 열에서만 동작하도록 작성됐다면, 실제 편집 위치가 그 조건을 통과하는지 살펴봐요. 한 셀 입력과 여러 셀 붙여넣기도 구분해요. Google의 편집 이벤트 객체에서 `range`는 바뀐 셀 또는 범위를 알려 주지만, `value`와 `oldValue`는 단일 셀 편집일 때에만 제공된다고 문서화돼 있어요. 여러 칸을 붙여넣은 뒤 그 값이 없다고 트리거 자체가 안 돌았다고 단정하면 진단이 엇나가요.

사람이 직접 고친 셀 값과 Apps Script 또는 외부 API가 쓴 값을 구별하는 자체 제작 도해
값 변화와 편집 이벤트의 입력 경로를 구별해요

설치형 편집 트리거로 바꿔도 API 쓰기는 이벤트가 아니에요

단순 `onEdit`가 권한 필요한 서비스를 쓰지 못해 설치형 편집 트리거를 찾는 경우가 있어요. Google은 설치형 트리거가 더 많은 기능과 승인 서비스를 사용할 수 있다고 설명해요. 하지만 설치형으로 바꾸는 것과 어떤 사건이 트리거를 일으키는지는 별개예요. 공식 설치형 트리거 문서도 스크립트 실행과 API 요청은 트리거를 일으키지 않는다고 명시해요. 설치형으로 새로 등록하면 외부 연동의 `setValue()`까지 감시할 수 있다고 기대하지 마세요.

Google은 설치형 트리거가 만든 사람의 계정으로 실행된다고도 설명해요. 팀원이 시트 셀을 편집해도 설치형 함수의 권한 주체는 트리거 생성자일 수 있어요. 이 점은 “어느 계정으로 실행됐나”를 설명하지만 프로그램이 쓴 셀에 편집 이벤트가 생긴다는 뜻은 아니에요. 권한 문제를 해결하려고 다른 사람에게 계정 정보를 요구하거나 트리거를 여러 개 중복 등록하기 전에, 사건이 애초에 편집 트리거 대상인지 확인해요.

이미 단순 트리거와 설치형 트리거가 모두 있다면 같은 사람 편집에 두 동작이 겹칠 수 있어요. Google 문서는 설치형 트리거를 여러 계정이 만들 수 있고, 한 계정에서는 다른 계정이 만든 트리거를 볼 수 없다고 해요. 무응답을 고치려다 중복 메일을 만드는 상황을 막으려면 현재 등록 상태와 실행 주체를 먼저 기록해야 해요. 이 글에서는 다른 계정의 권한을 우회하는 방법이나 보이지 않는 트리거를 지우는 방법을 제안하지 않아요.

단순 onEdit와 설치형 편집 트리거의 권한 차이와 스크립트 API 쓰기 공통 제한을 보여 주는 자체 제작 도해
설치형 트리거도 프로그램 쓰기는 편집으로 보지 않아요

변경 트리거와 양식 제출도 다른 사건이에요

“편집이 아니면 변경 트리거를 쓰면 되지 않을까?”라는 생각이 들 수 있어요. Google은 설치형 시트 변경 트리거의 이벤트 객체에 `changeType`을 제공하고, 값으로 `EDIT`, `INSERT_ROW`, `INSERT_COLUMN`, `FORMAT` 등을 열거해요. 설치형 편집 트리거의 이벤트 객체는 편집된 `range`와 단일 셀일 때의 새 값·이전 값을 담아요. 무엇이 바뀌었는지와 어느 셀을 편집했는지를 같은 데이터라고 취급하면 안 돼요.

시트 변경 트리거의 이름에 “변경”이 들어가도 스크립트나 API의 쓰기를 자동으로 감시한다는 뜻은 아니에요. Google의 설치형 트리거 공통 제한이 그대로 적용돼요. 시트의 행 삽입이나 서식 변화처럼 사건 종류가 다를 때에는 변경 트리거의 용도가 있지만, 값이 프로그램에서 들어왔다는 문제를 이름만 바꿔 해결하지는 못해요. 트리거 종류를 바꾸기 전에는 필요한 이벤트 객체가 `range`인지 `changeType`인지도 정해요.

Google Forms의 실제 제출에 반응해야 한다면 시트 편집 트리거와 시트의 양식 제출 트리거를 혼동하지 마세요. Google은 양식 제출을 별도의 설치형 트리거 종류로 열거하고, 시트 쪽 제출 이벤트 객체에는 질문 이름별 값과 제출 행의 범위가 있다고 설명해요. 그렇다고 코드가 `FormResponse.submit()`을 호출해 만든 제출까지 항상 트리거가 된다는 뜻은 아니에요. 공식 설치형 제한 문서는 그 호출이 양식 제출 트리거를 일으키지 않는 예를 듭니다.

독자에게 필요한 것은 “시트에 새 행이 보였다”라는 결과에서 한 단계 더 들어가는 일이에요. 사람이 셀을 편집했는지, 사용자가 양식을 제출했는지, 자동화가 API로 새 행을 만들었는지 각각 확인해요. 원본 시스템의 활동 기록과 입력 시각을 맞춰 보면 어떤 연결 지점에 후속 처리를 붙여야 할지 드러나요. 원래 사건을 모른 채 `onEdit`·`onChange`·양식 제출 트리거를 모두 켜는 방식은 진단보다 중복 실행 가능성만 키워요.

구글 시트 편집 변경 양식 제출 트리거와 각각 제공하는 이벤트 데이터를 비교하는 자체 제작 도해
편집·변경·제출 이벤트에서 필요한 데이터를 고르세요

프로그램이 값을 쓴다면 호출 지점을 다시 설계해요

다른 Apps Script가 시트에 값을 적는 구조라면 값을 쓰는 코드가 후속 처리 함수를 명시적으로 호출하도록 설계할 수 있어요. 이것은 `onEdit`가 자동으로 다시 도는 것과 달라요. 예를 들어 “상태 값을 기록한다”와 “해당 행을 처리한다”를 별도 함수로 나눠, 사용자 편집 경로에서는 편집 이벤트가 후속 함수를 부르고, 자동 입력 경로에서는 쓰기 성공 뒤 같은 후속 함수를 부르는 식이에요. 이 구조는 시스템에 실제로 있는 입력 경로를 모두 파악한 뒤에만 적용해요.

외부 서비스가 Sheets API로 값을 쓴다면 그 서비스의 성공 응답만으로 시트의 후속 업무가 끝났다고 볼 수 없어요. 시트 쓰기와 후속 처리의 책임을 어느 시스템이 맡을지 정해야 해요. 외부 연동이 후속 작업을 직접 호출하거나, 별도 시간 기반 점검 작업이 새 행을 읽도록 구성하는 방법이 있을 수 있어요. 다만 시간 기반 트리거는 이벤트가 발생한 즉시 실행된다고 보장하지 않아요. Google은 정기 시간 트리거의 시각이 어느 정도 무작위화될 수 있다고 설명해요.

후속 처리를 분리했다면 재시도와 중복을 고려해요. 같은 행을 두 번 처리해도 괜찮은지, 처리한 행을 어떻게 구분할지, 오류가 나면 어느 단계부터 다시 시작할지 먼저 적어요. 예시의 행 색 변경은 다시 해도 큰 문제가 없을 수 있지만 알림 발송이나 외부 시스템 등록은 다를 수 있어요. 실제 데이터를 재처리하기 전에 입력 통로 하나와 시험용 행 하나를 골라 기대 결과를 확인하는 편이 좋아요. 필요 없는 결제나 새로운 플랜은 해결책이 아니에요.

사람 편집인데도 반응이 없다면 다른 갈래를 보세요. 단순 트리거는 승인 필요한 서비스를 사용할 수 없고, 실행 시간도 30초 제한이 있다고 Google은 설명해요. `onEdit`는 대기 중인 이벤트를 최대 두 개까지만 큐에 넣는다고도 안내해요. 오류나 빠른 대량 편집 때문에 놓친 것처럼 보일 수 있으니 함수 실행 기록과 오류를 확인해야 해요. 이 제한을 핑계로 원본 이벤트 유형이 다른 문제까지 모두 같은 원인으로 묶지 마세요.

점검 결과를 표 대신 짧은 문장으로 남겨도 돼요. “10시 12분 A3 셀을 사람이 직접 수정했고 편집 이벤트의 범위는 A3였다”, “10시 15분 다른 스크립트가 `setValue()`로 B3를 썼으므로 편집 이벤트는 기대하지 않는다”처럼 적어요. 시간, 경로, 셀 범위, 기대한 트리거 종류를 함께 남기면 설정을 바꿨을 때 무엇이 개선됐는지도 비교하기 쉬워져요. 여러 자동화가 얽힌 시트라면 후속 업무의 책임 주체까지 한 줄 더 적어요.

정리하면 셀에 새 값이 보이는 것과 `onEdit` 이벤트가 발생하는 것은 같지 않아요. 사람의 편집인지, 스크립트·API의 쓰기인지, 양식 제출인지 확인한 다음 필요한 트리거와 이벤트 데이터를 선택해요. 설치형 편집 트리거는 권한 기능을 넓히지만 프로그램 쓰기를 사람 편집으로 바꾸지 않아요. 자동 입력 뒤의 후속 작업이 필요하다면 원래 값을 쓰는 지점에서 명시적으로 이어 주는 구조를 검토하는 것이 더 정확해요.

공식 근거: Google for Developers ‘Simple Triggers’(https://developers.google.com/apps-script/guides/triggers), ‘Installable Triggers’(https://developers.google.com/apps-script/guides/triggers/installable), ‘Event Objects’(https://developers.google.com/apps-script/guides/triggers/events), ‘SpreadsheetTriggerBuilder’(https://developers.google.com/apps-script/reference/script/spreadsheet-trigger-builder).

관심 주제의 주간 소식

새 글과 수정 소식은 주 1회 이하. 이메일 확인 후 구독 시작, 메일의 링크에서 구독 해제.

관심 주제 · 최대 3개

이메일은 구독 확인과 소식 전송에만 사용. 미확인 주소는 24시간 후 정리. 구독 해제 시 새 발송 중단과 이메일 삭제. 이미 전송 중인 메일은 취소 불가. 개인정보 안내