google.script.run
는 HTML 서비스 페이지에서 서버 측 Apps Script 함수를 호출할 수 있는 비동기 클라이언트 측 JavaScript API입니다. 다음 예는 google.script.run
의 가장 기본적인 기능인 클라이언트 측 JavaScript에서 서버에서 함수를 호출하는 것을 보여줍니다.
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function doSomething() { Logger.log('I was called!'); }
index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> google.script.run.doSomething(); </script> </head> </html>
이 스크립트를 웹 앱으로 배포하고 URL을 방문하면 아무것도 표시되지 않지만 로그를 보면 서버 함수 doSomething()
가 호출된 것을 확인할 수 있습니다.
서버 측 함수에 대한 클라이언트 측 호출은 비동기식입니다. 브라우저가 서버에 doSomething()
함수를 실행하도록 요청한 후에는 응답을 기다리지 않고 즉시 다음 줄의 코드로 넘어갑니다. 즉, 서버 함수 호출이 예상한 순서로 실행되지 않을 수 있습니다. 두 함수를 동시에 호출하면 먼저 실행되는 함수를 알 수 있는 방법이 없습니다. 페이지를 로드할 때마다 결과가 다를 수 있습니다. 이 경우 성공 핸들러와 실패 핸들러가 코드 흐름을 제어하는 데 도움이 됩니다.
google.script.run
API를 사용하면 서버 함수를 동시에 10번 호출할 수 있습니다. 10개가 아직 실행 중인 상태에서 11번째 호출을 하면 10개 중 한 곳이 해제될 때까지 서버 함수가 지연됩니다. 실제로는 이 제한에 대해 생각할 일이 거의 없습니다. 특히 대부분의 브라우저는 이미 동일한 서버에 대한 동시 요청 수를 10개 미만으로 제한하기 때문입니다.
예를 들어 Firefox에서는 한도가 6개입니다. 대부분의 브라우저도 마찬가지로 기존 요청 중 하나가 완료될 때까지 초과 서버 요청을 지연시킵니다.
매개변수 및 반환 값
클라이언트의 매개변수를 사용하여 서버 함수를 호출할 수 있습니다. 마찬가지로 서버 함수는 성공 핸들러에 전달된 매개변수로 클라이언트에 값을 반환할 수 있습니다.
유효한 매개변수 및 반환 값은 Number
, Boolean
, String
, null
과 같은 JavaScript 기본 요소와 기본 요소, 객체, 배열로 구성된 JavaScript 객체 및 배열입니다. 페이지 내의 form
요소도 매개변수로 유효하지만 함수의 유일한 매개변수여야 하며 반환 값으로는 유효하지 않습니다. form
이외의 Date
, Function
, DOM 요소 또는 객체나 배열 내의 금지된 유형을 포함한 기타 금지된 유형을 전달하려고 하면 요청이 실패합니다. 순환 참조를 만드는 객체도 실패하며 배열 내 정의되지 않은 필드는 null
이 됩니다.
서버에 전달된 객체는 원본의 사본이 됩니다. 서버 함수가 객체를 수신하고 속성을 변경해도 클라이언트의 속성은 영향을 받지 않습니다.
성공 핸들러
클라이언트 측 코드는 서버 호출이 완료될 때까지 기다리지 않고 다음 줄로 계속 진행되므로 withSuccessHandler(function)
를 사용하면 서버가 응답할 때 실행할 클라이언트 측 콜백 함수를 지정할 수 있습니다. 서버 함수가 값을 반환하면 API는 값을 새 함수에 매개변수로 전달합니다.
다음 예에서는 서버가 응답할 때 브라우저 알림을 표시합니다. 서버 측 함수가 Gmail 계정에 액세스하므로 이 코드 샘플에는 승인이 필요합니다. 스크립트를 승인하는 가장 간단한 방법은 페이지를 로드하기 전에 스크립트 편집기에서 getUnreadEmails()
함수를 한 번 직접 실행하는 것입니다. 또는 웹 앱을 배포할 때 '웹 앱에 액세스하는 사용자'로 실행하도록 선택할 수 있습니다. 이 경우 앱을 로드할 때 승인 메시지가 표시됩니다.
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getUnreadEmails() { return GmailApp.getInboxUnreadCount(); }
index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> function onSuccess(numUnread) { var div = document.getElementById('output'); div.innerHTML = 'You have ' + numUnread + ' unread messages in your Gmail inbox.'; } google.script.run.withSuccessHandler(onSuccess) .getUnreadEmails(); </script> </head> <body> <div id="output"></div> </body> </html>
실패 핸들러
서버가 응답하지 않거나 오류를 발생시키는 경우 withFailureHandler(function)
를 사용하면 성공 핸들러 대신 실패 핸들러를 지정할 수 있으며, 이때 Error
객체 (있는 경우)가 인수로 전달됩니다.
기본적으로 실패 핸들러를 지정하지 않으면 실패가 JavaScript 콘솔에 로깅됩니다. 이를 재정의하려면 withFailureHandler(null)
를 호출하거나 아무것도 하지 않는 실패 핸들러를 제공합니다.
이 예에서 볼 수 있듯이 실패 핸들러의 문법은 성공 핸들러와 거의 동일합니다.
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getUnreadEmails() { // 'got' instead of 'get' will throw an error. return GmailApp.gotInboxUnreadCount(); }
index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> function onFailure(error) { var div = document.getElementById('output'); div.innerHTML = "ERROR: " + error.message; } google.script.run.withFailureHandler(onFailure) .getUnreadEmails(); </script> </head> <body> <div id="output"></div> </body> </html>
사용자 객체
withUserObject(object)
를 호출하여 핸들러에 두 번째 매개변수로 전달할 객체를 지정하면 서버를 여러 번 호출할 때 동일한 성공 또는 실패 핸들러를 재사용할 수 있습니다.
이 '사용자 객체'(User
클래스와 혼동하지 마세요)를 사용하면 클라이언트가 서버에 연결된 컨텍스트에 응답할 수 있습니다. 사용자 객체는 서버로 전송되지 않으므로 서버 호출의 매개변수 및 반환 값에 대한 제한 없이 함수, DOM 요소 등을 포함하여 거의 모든 것이 될 수 있습니다. 그러나 사용자 객체는 new
연산자로 생성된 객체가 될 수 없습니다.
이 예시에서 두 버튼 중 하나를 클릭하면 두 버튼이 하나의 성공 핸들러를 공유하더라도 서버의 값으로 해당 버튼이 업데이트되고 다른 버튼은 변경되지 않습니다. onclick
핸들러 내에서 this
키워드는 button
자체를 나타냅니다.
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getEmail() { return Session.getActiveUser().getEmail(); }
index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> function updateButton(email, button) { button.value = 'Clicked by ' + email; } </script> </head> <body> <input type="button" value="Not Clicked" onclick="google.script.run .withSuccessHandler(updateButton) .withUserObject(this) .getEmail()" /> <input type="button" value="Not Clicked" onclick="google.script.run .withSuccessHandler(updateButton) .withUserObject(this) .getEmail()" /> </body> </html>
양식
form
요소를 매개변수로 사용하여 서버 함수를 호출하면 양식이 필드 이름을 키로, 필드 값을 값으로 사용하는 단일 객체가 됩니다. 값은 모두 문자열로 변환됩니다. 단, file-input 필드의 콘텐츠는 Blob
객체가 됩니다.
이 예에서는 페이지를 새로고침하지 않고 파일 입력란을 포함한 양식을 처리합니다. 파일을 Google Drive에 업로드한 다음 클라이언트 측 페이지에 파일의 URL을 출력합니다. onsubmit
핸들러 내에서 this
키워드는 양식 자체를 나타냅니다. 페이지의 모든 양식을 로드하면 preventFormSubmit
에 의해 기본 제출 작업이 사용 중지됩니다. 이렇게 하면 예외가 발생할 때 페이지가 부정확한 URL로 리디렉션되지 않습니다.
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function processForm(formObject) { var formBlob = formObject.myFile; var driveFile = DriveApp.createFile(formBlob); return driveFile.getUrl(); }
index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> // Prevent forms from submitting. function preventFormSubmit() { var forms = document.querySelectorAll('form'); for (var i = 0; i < forms.length; i++) { forms[i].addEventListener('submit', function(event) { event.preventDefault(); }); } } window.addEventListener('load', preventFormSubmit); function handleFormSubmit(formObject) { google.script.run.withSuccessHandler(updateUrl).processForm(formObject); } function updateUrl(url) { var div = document.getElementById('output'); div.innerHTML = '<a href="' + url + '">Got it!</a>'; } </script> </head> <body> <form id="myForm" onsubmit="handleFormSubmit(this)"> <input name="myFile" type="file" /> <input type="submit" value="Submit" /> </form> <div id="output"></div> </body> </html>
스크립트 실행기
google.script.run
는 '스크립트 실행자'의 빌더라고 생각할 수 있습니다. 스크립트 실행자에 성공 핸들러, 실패 핸들러 또는 사용자 객체를 추가하면 기존 실행자가 변경되지 않습니다. 대신 새로운 동작이 포함된 새 스크립트 실행자가 반환됩니다.
withSuccessHandler()
, withFailureHandler()
, withUserObject()
의 조합과 순서를 자유롭게 사용할 수 있습니다. 이미 값이 설정된 스크립트 런너에서 수정 함수를 호출할 수도 있습니다. 새 값이 이전 값을 덮어씁니다.
이 예에서는 세 가지 서버 호출 모두에 공통 실패 핸들러를 설정하지만 두 가지 별도의 성공 핸들러를 설정합니다.
var myRunner = google.script.run.withFailureHandler(onFailure);
var myRunner1 = myRunner.withSuccessHandler(onSuccess);
var myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);
myRunner1.doSomething();
myRunner1.doSomethingElse();
myRunner2.doSomething();
비공개 함수
이름이 밑줄로 끝나는 서버 함수는 비공개로 간주됩니다.
이러한 함수는 google.script
에서 호출할 수 없으며 이름이 클라이언트로 전송되지 않습니다. 따라서 이를 사용하여 서버에서 비밀로 유지해야 하는 구현 세부정보를 숨길 수 있습니다. google.script
는 라이브러리 내의 함수와 스크립트의 최상위 수준에서 선언되지 않은 함수도 볼 수 없습니다.
이 예에서 getBankBalance()
함수는 클라이언트 코드에서 사용할 수 있습니다. 소스 코드를 검사하는 사용자는 개발자가 호출하지 않더라도 함수의 이름을 확인할 수 있습니다. 그러나 deepSecret_()
및 obj.objectMethod()
함수는 클라이언트에 완전히 표시되지 않습니다.
Code.gs
function doGet() { return HtmlService.createHtmlOutputFromFile('Index'); } function getBankBalance() { var email = Session.getActiveUser().getEmail() return deepSecret_(email); } function deepSecret_(email) { // Do some secret calculations return email + ' has $1,000,000 in the bank.'; } var obj = { objectMethod: function() { // More secret calculations } };
index.html
<!DOCTYPE html> <html> <head> <base target="_top"> <script> function onSuccess(balance) { var div = document.getElementById('output'); div.innerHTML = balance; } google.script.run.withSuccessHandler(onSuccess) .getBankBalance(); </script> </head> <body> <div id="output">No result yet...</div> </body> </html>
애플리케이션에서 대화상자 크기 조절
Google Docs, Sheets 또는 Forms의 맞춤 대화상자 크기는 클라이언트 측 코드에서 google.script.host
메서드 setWidth(width)
또는 setHeight(height)
를 호출하여 조정할 수 있습니다. 대화상자의 초기 크기를 설정하려면 HtmlOutput
메서드 setWidth(width)
및 setHeight(height)
를 사용하세요. 대화상자의 크기를 조절해도 상위 창의 중앙에 다시 배치되지 않으며 사이드바의 크기는 조절할 수 없습니다.
에서 대화상자 및 사이드바 닫기
HTML 서비스를 사용하여 Google Docs, Sheets 또는 Forms에 대화상자 또는 사이드바를 표시하는 경우 window.close()
를 호출하여 인터페이스를 닫을 수 없습니다. 대신 google.script.host.close()
를 호출해야 합니다.
예를 보려면 HTML을 사용자 인터페이스로 제공 섹션을 참고하세요.
브라우저 포커스 이동
사용자의 브라우저에서 대화상자 또는 사이드바에서 Google Docs, Sheets 또는 Forms 편집기로 포커스를 전환하려면 google.script.host.editor.focus()
메서드를 호출하면 됩니다.
이 메서드는 문서 서비스 메서드 Document.setCursor(position)
및 Document.setSelection(range)
와 함께 사용하면 특히 유용합니다.