Rhino 런타임을 사용하는 기존 스크립트가 있고 스크립트를 V8로 이전해야 합니다.
Rhino 런타임을 사용하여 작성된 대부분의 스크립트는 V8 런타임을 사용하여 작동할 수 있습니다. 조정할 수 없습니다. 종종 V8 구문을 추가하기 위한 유일한 전제조건은 몇 가지 기능을 V8 런타임 사용 설정
하지만 Google Cloud의 비호환성 스크립트 작성으로 이어질 수 있는 기타 차이점 오류가 발생하거나 예기치 않게 동작하는 것을 확인할 수 있습니다. 이전 시 스크립트 프로젝트에서 이러한 문제를 검색하고 바로잡는 것입니다.
V8 이전 절차
스크립트를 V8로 마이그레이션하려면 다음 절차를 따르세요.
- V8 런타임 사용 설정 로 설정합니다.
- 비호환성을 주의 깊게 검토하세요. 참조하세요. 스크립트를 검토하여 비호환성이 존재합니다. 하나 이상의 비호환성이 있는 경우 스크립트 코드를 조정하여 문제를 제거하거나 피하세요.
- 아래 나열된 기타 차이점을 주의 깊게 검토하세요. 스크립트를 검토하여 나열된 차이점 중 코드의 동작에 영향을 미치는 차이점이 있는지 확인합니다. 스크립트를 조정하여 동작을 수정합니다.
- 발견된 비호환성을 수정하거나 코드가 달라졌으므로 코드를 업데이트하여 V8 구문 및 기타 기능 변경할 수 있습니다
- 코드 조정을 완료한 후에는 스크립트를 철저히 테스트하여 정상적으로 작동하는지 확인할 수 있습니다
- 스크립트가 웹 앱 또는 게시된 부가기능인 경우 반드시 새 버전 만들기 스크립트의 한 부분을 들을 수 있습니다. 다양한 기기에서 사용자는 이 버전으로 스크립트를 다시 게시해야 합니다.
비호환성
안타깝게도 기존 Rhino 기반 Apps Script 런타임은 여러 비표준 ECMAScript 동작을 허용했습니다. V8은 표준을 준수하므로 이전 후에는 지원되지 않습니다 이러한 문제를 수정하지 않으면 V8 런타임이 사용 설정된 후 오류가 발생하거나 스크립트 동작이 손상됩니다.
다음 섹션에서는 이러한 각 동작과 취해야 할 단계를 설명합니다. 스크립트 코드를 수정하세요.
for each(variable in object)
사용 자제
이
for each (variable in object)
드림
문이 JavaScript 1.6에 추가되었으며 for...of
로 대체되었습니다.
스크립트를 V8로 이전할 때 for each (variable in object)
문으로 구성되어 있습니다.
대신 for (variable in object)
를 사용하세요.
// Rhino runtime var obj = {a: 1, b: 2, c: 3}; // Don't use 'for each' in V8 for each (var value in obj) { Logger.log("value = %s", value); } |
// V8 runtime var obj = {a: 1, b: 2, c: 3}; for (var key in obj) { // OK in V8 var value = obj[key]; Logger.log("value = %s", value); } |
Date.prototype.getYear()
사용 자제
원래 Rhino 런타임에서는
Date.prototype.getYear()
드림
1900-1999 사이의 연도에 대해서는 2자리 연도를 반환하지만, 1900-1999 사이의 연도에 대해서는 4자리 연도를 반환합니다.
JavaScript 1.2 이하의 동작이었습니다.
V8 런타임에서
Date.prototype.getYear()
드림
는
ECMAScript 표준입니다.
스크립트를 V8로 이전할 때는 항상
Date.prototype.getFullYear()
,
이 함수는 날짜와 관계없이 4자리 연도를 반환합니다.
예약된 키워드를 이름으로 사용하지 않기
ECMAScript는 함수 및 변수 이름에 특정 예약된 키워드를 사용하는 것을 금지합니다. Rhino 런타임은 이러한 단어를 많이 허용했습니다. 따라서 코드에서 이를 사용하는 경우 함수 또는 변수의 이름을 바꿔야 합니다.
스크립트를 V8로 이전할 때 변수 또는 함수의 이름을 지정하지 마세요.
다음 중 하나를 사용하여
예약 키워드를 사용합니다.
키워드 이름을 사용하지 않도록 변수 또는 함수의 이름을 바꿉니다. 일반적인 용도
개의 키워드 중 class
, import
, export
가 포함됩니다.
const
변수 재할당 방지
원래 Rhino 런타임에서는 const
를 사용하여 변수를 선언할 수 있습니다.
기호의 값이 절대 변경되지 않으며
기호는 무시됩니다.
새 V8 런타임에서 const
키워드는 표준을 준수하며
const
로 선언된 변수에 추가하면
TypeError: Assignment to constant variable
런타임 오류입니다.
스크립트를 V8으로 이전할 때 const
변수의 값을 다시 할당하지 마세요.
// Rhino runtime const x = 1; x = 2; // No error console.log(x); // Outputs 1 |
// V8 runtime const x = 1; x = 2; // Throws TypeError console.log(x); // Never executed |
XML 리터럴 및 XML 객체 피하기
이 비표준 확장자 ECMAScript를 사용하면 Apps Script 프로젝트에서 XML 구문을 직접 사용할 수 있습니다.
스크립트를 V8로 마이그레이션할 때 직접적인 XML 리터럴 또는 XML 객체를 사용하세요.
대신 XmlService를 사용하여 XML을 파싱합니다.
// V8 runtime var incompatibleXml1 = <container><item/></container>; // Don't use var incompatibleXml2 = new XML('<container><item/></container>'); // Don't use var xml3 = XmlService.parse('<container><item/></container>'); // OK |
__iterator__
를 사용하여 커스텀 반복기 함수를 빌드하지 않음
JavaScript 1.7에서는 모든 cla에 맞춤 반복자를 추가할 수 있는 기능이 추가되었습니다.
해당 클래스의 프로토타입에서 __iterator__
함수를 선언하면 됩니다. 이었습니다
도 개발자의 편의를 위해 Apps Script의 Rhino 런타임에 추가되었습니다. 하지만
이 기능은
ECMA-262 표준
ECMAScript 호환 JavaScript 엔진에서 제거되었습니다. V8을 사용하는 스크립트
이 반복기 구성을 사용할 수 없습니다.
스크립트를 V8로 마이그레이션할 때는 __iterator__
함수를 빌드하지 마세요.
커스텀 반복자. 대신
ECMAScript 6 반복자를 사용합니다.
다음 배열 구성을 고려해 보겠습니다.
// Create a sample array var myArray = ['a', 'b', 'c']; // Add a property to the array myArray.foo = 'bar'; // The default behavior for an array is to return keys of all properties, // including 'foo'. Logger.log("Normal for...in loop:"); for (var item in myArray) { Logger.log(item); // Logs 0, 1, 2, foo } // To only log the array values with `for..in`, a custom iterator can be used. |
다음 코드 예는 Rhino 런타임, V8 런타임에서 대체 반복자를 구성하는 방법에 대해 알아보세요.
// Rhino runtime custom iterator function ArrayIterator(array) { this.array = array; this.currentIndex = 0; } ArrayIterator.prototype.next = function() { if (this.currentIndex >= this.array.length) { throw StopIteration; } return "[" + this.currentIndex + "]=" + this.array[this.currentIndex++]; }; // Direct myArray to use the custom iterator myArray.__iterator__ = function() { return new ArrayIterator(this); } Logger.log("With custom Rhino iterator:"); for (var item in myArray) { // Logs [0]=a, [1]=b, [2]=c Logger.log(item); } |
// V8 runtime (ECMAScript 6) custom iterator myArray[Symbol.iterator] = function() { var currentIndex = 0; var array = this; return { next: function() { if (currentIndex < array.length) { return { value: "[${currentIndex}]=" + array[currentIndex++], done: false}; } else { return {done: true}; } } }; } Logger.log("With V8 custom iterator:"); // Must use for...of since // for...in doesn't expect an iterable. for (var item of myArray) { // Logs [0]=a, [1]=b, [2]=c Logger.log(item); } |
조건부 catch 절 피하기
V8 런타임은 표준을 준수하지 않으므로 catch..if
조건부 catch 절을 지원하지 않습니다.
스크립트를 V8으로 이전할 때는 catch 본문 내에 catch 조건문을 이동합니다.
// Rhino runtime try { doSomething(); } catch (e if e instanceof TypeError) { // Don't use // Handle exception } |
// V8 runtime try { doSomething(); } catch (e) { if (e instanceof TypeError) { // Handle exception } } |
Object.prototype.toSource()
사용 지양
JavaScript 1.3에는 Object.prototype.toSource() 메서드를 호출할 수 있습니다. V8 런타임에서는 지원되지 않습니다.
스크립트를 V8로 이전할 때 Object.prototype.toSource() 추출해야 합니다.
그 밖의 차이점
스크립트 오류를 일으킬 수 있는 위와 같은 비호환성 외에도 수정되지 않을 경우 예상치 못한 V8로 이어질 수 있는 몇 가지 다른 차이점이 있습니다. 런타임 스크립트 동작을 지원합니다.
다음 섹션에서는 이러한 예상치 못한 문제를 방지하기 위해 스크립트 코드를 업데이트하는 방법을 설명합니다.
언어별 날짜 및 시간 형식 조정
Date
메서드 toLocaleString()
, toLocaleDateString()
, toLocaleTimeString()
는 Rhino와 비교하여 V8 런타임에서 다르게 동작합니다.
Rhino에서 기본 형식은 긴 형식이며 모든 매개변수는 무시됩니다.
V8 런타임에서 기본 형식은 짧은 형식 및 매개변수입니다.
ECMA 표준에 따라 처리됩니다 (자세한 내용은
toLocaleDateString()
문서
참조하세요.
스크립트를 V8로 이전할 때 코드의 기대치 테스트 및 조정 언어별 날짜 및 시간 메서드의 출력과 관련된 내용을 참고하세요.
// Rhino runtime var event = new Date( Date.UTC(2012, 11, 21, 12)); // Outputs "December 21, 2012" in Rhino console.log(event.toLocaleDateString()); // Also outputs "December 21, 2012", // ignoring the parameters passed in. console.log(event.toLocaleDateString( 'de-DE', { year: 'numeric', month: 'long', day: 'numeric' })); |
// V8 runtime var event = new Date( Date.UTC(2012, 11, 21, 12)); // Outputs "12/21/2012" in V8 console.log(event.toLocaleDateString()); // Outputs "21. Dezember 2012" console.log(event.toLocaleDateString( 'de-DE', { year: 'numeric', month: 'long', day: 'numeric' })); |
Error.fileName
및 Error.lineNumber
사용 피하기
V8 이전 버전에서는
Error
드림
객체는 fileName
또는 lineNumber
를 생성자 매개변수로 지원하지 않습니다.
객체 속성일 수도 있습니다
스크립트를 V8로 이전할 때
Error.fileName
및 Error.lineNumber
에 관한 종속 항목을 삭제합니다.
대안은
Error.prototype.stack
이 스택은 표준이 아니지만 Rhino와 V8에서 모두 지원됩니다. 이
두 플랫폼에서 생성되는 스택 트레이스의 형식은 약간 다릅니다.
// Rhino runtime Error.prototype.stack // stack trace format at filename:92 (innerFunction) at filename:97 (outerFunction) |
// V8 runtime Error.prototype.stack // stack trace format Error: error message at innerFunction (filename:92:11) at outerFunction (filename:97:5) |
문자열로화된 enum 객체의 처리 조정
원래 Rhino 런타임에서는 JavaScript를 사용해
JSON.stringify()
드림
메서드는 {}
만 반환합니다.
V8에서는 enum 객체에서 동일한 메서드를 사용하면 enum 이름이 다시 반환됩니다.
스크립트를 V8로 이전할 때
출력과 관련하여 코드의 기대치를 테스트하고
JSON.stringify()
자세히 알아보기:
// Rhino runtime var enumName = JSON.stringify(Charts.ChartType.BUBBLE); // enumName evaluates to {} |
// V8 runtime var enumName = JSON.stringify(Charts.ChartType.BUBBLE); // enumName evaluates to "BUBBLE" |
정의되지 않은 매개변수 처리 조정
원래 Rhino 런타임에서 메서드에 undefined
을 매개변수로 전달
결과적으로 "undefined"
문자열을 해당 메서드에 전달했습니다.
V8에서 undefined
를 메서드에 전달하는 것은 null
를 전달하는 것과 같습니다.
스크립트를 V8로 이전할 때
undefined
매개변수와 관련된 코드 기대치를 테스트하고 조정합니다.
// Rhino runtime SpreadsheetApp.getActiveRange() .setValue(undefined); // The active range now has the string // "undefined" as its value. |
// V8 runtime SpreadsheetApp.getActiveRange() .setValue(undefined); // The active range now has no content, as // setValue(null) removes content from // ranges. |
전역 this
처리 조정
Rhino 런타임은 이를 사용하는 스크립트에 대한 암시적 특수 컨텍스트를 정의합니다.
스크립트 코드는 실제 전역 변수와 구분되는 이 암시적 컨텍스트에서 실행됩니다.
this
즉, '전역 this
'에 대한 참조는 실제로 코드에
코드 및 변수만 포함된 특수 컨텍스트로 평가됩니다.
있습니다. 내장된 Apps Script 서비스 및 ECMAScript 객체는 이 this
사용에서 제외됩니다. 이 상황은 다음과 유사합니다.
JavaScript 구조:
// Rhino runtime // Apps Script built-in services defined here, in the actual global context. var SpreadsheetApp = { openById: function() { ... } getActive: function() { ... } // etc. }; function() { // Implicit special context; all your code goes here. If the global this // is referenced in your code, it only contains elements from this context. // Any global variables you defined. var x = 42; // Your script functions. function myFunction() { ... } // End of your code. }(); |
V8에서는 암시적 특수 컨텍스트가 삭제됩니다. 스크립트에 정의된 전역 변수와 함수는 내장 Apps Script 서비스 및 Math
및 Date
와 같은 ECMAScript 내장 함수 옆에 있는 전역 컨텍스트에 배치됩니다.
스크립트를 V8로 이전할 때는 전역 컨텍스트에서 this
사용과 관련된 코드의 기대치를 테스트하고 조정합니다. 대부분의 경우 차이점은 코드가 전역 this
객체의 키 또는 속성 이름을 검사하는 경우에만 분명하게 나타납니다.
// Rhino runtime var myGlobal = 5; function myFunction() { // Only logs [myFunction, myGlobal]; console.log(Object.keys(this)); // Only logs [myFunction, myGlobal]; console.log( Object.getOwnPropertyNames(this)); } |
// V8 runtime var myGlobal = 5; function myFunction() { // Logs an array that includes the names // of Apps Script services // (CalendarApp, GmailApp, etc.) in // addition to myFunction and myGlobal. console.log(Object.keys(this)); // Logs an array that includes the same // values as above, and also includes // ECMAScript built-ins like Math, Date, // and Object. console.log( Object.getOwnPropertyNames(this)); } |
라이브러리에서 instanceof
처리 조정
다음과 같이 매개변수로 전달된 객체의 라이브러리에서 instanceof
사용
함수를 사용하면 거짓음성이 발생할 수 있습니다. V8 런타임에서는
프로젝트와 라이브러리가 서로 다른 실행 컨텍스트에서 실행되므로
다른 전역 변수 및 프로토타입 체인이 포함될 수 있습니다.
이는 라이브러리에서 객체에 instanceof
를 사용하는 경우에만 해당됩니다.
API를 사용할 수 있습니다 Cloud Shell에서 생성된 객체에 사용하면
다른 스크립트에 있는 경우든 상관없이
정상적으로 작동할 것입니다.
V8에서 실행되는 프로젝트가 스크립트를 라이브러리로 사용하는 경우 스크립트가 다른 프로젝트에서 전달될 매개변수에 instanceof
를 사용하는지 확인합니다. 조정
instanceof
사용 및 용도에 따라 가능한 다른 대안을 사용합니다.
있습니다
a instanceof b
의 한 가지 대안은 a
의 생성자를 사용하는 것입니다.
전체 프로토타입 체인을 검색할 필요 없이
있습니다.
사용량: a.constructor.name == "b"
프로젝트 A가 프로젝트 B를 라이브러리로 사용하는 프로젝트 A와 프로젝트 B를 생각해 보세요.
//Rhino runtime //Project A function caller() { var date = new Date(); // Returns true return B.callee(date); } //Project B function callee(date) { // Returns true return(date instanceof Date); } |
//V8 runtime //Project A function caller() { var date = new Date(); // Returns false return B.callee(date); } //Project B function callee(date) { // Incorrectly returns false return(date instanceof Date); // Consider using return (date.constructor.name == // “Date”) instead. // return (date.constructor.name == “Date”) -> Returns // true } |
또 다른 대안은 기본 프로젝트에서 instanceof
를 확인하는 함수를 도입하는 것입니다.
라이브러리 함수를 호출할 때 다른 매개변수와 함께 함수를 전달합니다. 전달된 함수
그런 다음 라이브러리 내에서 instanceof
를 확인하는 데 사용할 수 있습니다.
//V8 runtime //Project A function caller() { var date = new Date(); // Returns True return B.callee(date, date => date instanceof Date); } //Project B function callee(date, checkInstanceOf) { // Returns True return checkInstanceOf(date); } |
라이브러리로의 비공유 리소스 전달 조정
기본 스크립트에서 라이브러리로 비공유 리소스를 전달하는 작업은 V8 런타임에서 다르게 작동합니다.
Rhino 런타임에서는 비공유 리소스를 전달해도 작동하지 않습니다. 대신 라이브러리는 자체 리소스를 사용합니다.
V8 런타임에서는 공유되지 않는 리소스를 라이브러리에 전달해도 됩니다. 라이브러리가 전달된 비공유 리소스를 사용합니다.
공유되지 않는 리소스를 함수 매개변수로 전달하지 마세요. 공유되지 않은 리소스는 해당 리소스를 사용하는 스크립트에서 항상 선언하세요.
프로젝트 A가 프로젝트 B를 라이브러리로 사용하는 프로젝트 A와 프로젝트 B를 생각해 보세요. 이 예에서 PropertiesService
는 공유되지 않는 리소스입니다.
// Rhino runtime // Project A function testPassingNonSharedProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-A'); B.setScriptProperties(); // Prints: Project-B Logger.log(B.getScriptProperties( PropertiesService, 'project')); } |
// V8 runtime // Project A function testPassingNonSharedProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-A'); B.setScriptProperties(); // Prints: Project-A Logger.log(B.getScriptProperties( PropertiesService, 'project')); } |
독립형 스크립트에 대한 액세스 권한 업데이트
V8 런타임에서 실행되는 독립형 스크립트의 경우 사용자에게 최소한 스크립트의 트리거가 제대로 작동할 수 있도록 스크립트에 대한 보기 권한이 있어야 합니다.