이 가이드에서는 입력 변수를 검증하는 방법을 설명합니다.
입력 변수를 정의할 때는 사용자가 적절한 값을 입력하는지 확인하는 것이 좋습니다. 예를 들어 사용자에게 숫자를 입력하라고 요청하는 경우 a 대신 1을 입력하는지 확인하면 단계가 오류 없이 실행되는지 확인할 수 있습니다.
입력 변수를 검증하는 방법에는 두 가지가 있습니다.
- 클라이언트 측 유효성 검사: 클라이언트 측 유효성 검사를 사용하면 사용자의 기기에서 직접 사용자의 입력을 확인할 수 있습니다. 사용자는 즉시 피드백을 받고 단계를 구성하는 동안 입력의 오류를 수정할 수 있습니다.
- 서버 측 검증: 서버 측 검증을 사용하면 검증 중에 서버에서 로직을 실행할 수 있습니다. 이는 클라이언트에 없는 정보(예: 다른 시스템이나 데이터베이스의 데이터)를 조회해야 하는 경우에 유용합니다.
클라이언트 측 유효성 검사
클라이언트 측 유효성 검사를 구현하는 방법에는 두 가지가 있습니다.
- 위젯에 특정 수 미만의 문자가 포함되어 있는지 또는
@기호가 포함되어 있는지 확인하는 등의 기본 유효성 검사의 경우 Google Workspace 부가기능의 카드 서비스의Validation클래스를 호출합니다. - 위젯 값을 다른 위젯 값과 비교하는 등 강력한 검증을 위해
CardService를 사용하여 지원되는 다음 카드 위젯에 Common Expression Language (CEL) 검증을 추가할 수 있습니다.
Validation 클래스 호출
다음 예에서는 TextInput 위젯에 10자 이하의 문자가 포함되어 있는지 확인합니다.
Apps Script
const validation = CardService.newValidation().setCharacterLimit('10').setInputType(
CardService.InputType.TEXT);
추가 유효성 검사 옵션은 CEL 유효성 검사를 사용하세요.
CEL 유효성 검사
Common Expression Language (CEL) 검증은 다른 서비스의 데이터 조회에 종속되지 않는 입력 값 확인을 클라이언트 측으로 오프로드하여 서버 측 검증의 지연 시간 없이 즉각적인 입력 확인을 제공합니다.
CEL을 사용하여 유효성 검사 결과에 따라 위젯을 표시하거나 숨기는 등의 카드 동작을 만들 수도 있습니다. 이러한 동작은 사용자가 입력을 수정하는 데 도움이 되는 오류 메시지를 표시하거나 숨기는 데 유용합니다.
완전한 CEL 검증을 빌드하려면 다음 구성요소가 필요합니다.
ExpressionData(카드): 정의된 조건 중 하나가 충족될 때 지정된 유효성 검사 로직과 위젯 트리거링 로직을 포함합니다.Id: 현재 카드 내ExpressionData의 고유 식별자입니다.Expression: 검증 논리를 정의하는 CEL 문자열입니다 (예:"value1 == value2"))를 제공합니다.Conditions: 사전 정의된 검증 결과 (SUCCESS 또는 FAILURE)의 선택 항목이 포함된 조건 목록입니다. 조건은 공유된actionRuleId를 사용하여Triggers를 통해 위젯 측EventAction에 연결됩니다.- 카드 수준
EventAction: 카드에서 CEL 유효성 검사를 활성화하고 이벤트 후 트리거를 통해ExpressionData필드를 결과 위젯에 연결합니다.actionRuleId: 이EventAction의 고유 ID입니다.ExpressionDataAction: 이 작업이 CEL 평가를 시작함을 나타내려면START_EXPRESSION_EVALUATION로 설정합니다.Trigger:actionRuleId에 따라Conditions을 위젯 측EventActions에 연결합니다.
위젯 수준
EventAction: 성공 또는 실패 조건이 충족될 때 결과 위젯의 동작을 제어합니다. 예를 들어 결과 위젯은 유효성 검사에 실패할 때만 표시되는 오류 메시지가 포함된TextParagraph일 수 있습니다.actionRuleId: 카드 측Trigger의actionRuleId와 일치합니다.CommonWidgetAction: 위젯 표시 상태 업데이트와 같이 평가가 포함되지 않는 작업을 정의합니다.UpdateVisibilityAction: 위젯의 표시 상태 (VISIBLE 또는 HIDDEN)를 업데이트하는 작업입니다.
다음 예에서는 두 텍스트 입력이 동일한지 확인하기 위해 CEL 유효성 검사를 구현하는 방법을 보여줍니다. 같지 않으면 오류 메시지가 표시됩니다.
-
그림 1: failCondition이 충족되면 (입력이 같지 않음) 오류 메시지 위젯이VISIBLE로 설정되고 표시됩니다. -
그림 2: successCondition이 충족되면 (입력이 동일함) 오류 메시지 위젯이HIDDEN로 설정되고 표시되지 않습니다.
다음은 애플리케이션 코드와 JSON 매니페스트 파일의 예입니다.
Apps Script
function onConfig() {
// Create a Card
let card = CardService.newCardBuilder();
const textInput_1 = CardService.newTextInput()
.setTitle("Input number 1")
.setFieldName("value1"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.
const textInput_2 = CardService.newTextInput()
.setTitle("Input number 2")
.setFieldName("value2"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.
let sections = CardService.newCardSection()
.setHeader("Two number equals")
.addWidget(textInput_1)
.addWidget(textInput_2);
// CEL Validation
// Define Conditions
const condition_success = CardService.newCondition()
.setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID")
.setExpressionDataCondition(
CardService.newExpressionDataCondition()
.setConditionType(
CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_SUCCESS));
const condition_fail = CardService.newCondition()
.setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID")
.setExpressionDataCondition(
CardService.newExpressionDataCondition()
.setConditionType(
CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_FAILURE));
// Define Card-side EventAction
const expressionDataAction = CardService.newExpressionDataAction()
.setActionType(
CardService.ExpressionDataActionType.START_EXPRESSION_EVALUATION);
// Define Triggers for each Condition respectively
const trigger_success = CardService.newTrigger()
.setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID");
const trigger_failure = CardService.newTrigger()
.setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID");
const eventAction = CardService.newEventAction()
.setActionRuleId("CEL_TEXTINPUT_EVALUATION_RULE_ID")
.setExpressionDataAction(expressionDataAction)
.addPostEventTrigger(trigger_success)
.addPostEventTrigger(trigger_failure);
// Define ExpressionData for the current Card
const expressionData = CardService.newExpressionData()
.setId("expData_id")
.setExpression("value1 == value2") // CEL expression
.addCondition(condition_success)
.addCondition(condition_fail)
.addEventAction(eventAction);
card = card.addExpressionData(expressionData);
// Create Widget-side EventActions and a widget to display error message
const widgetEventActionFail = CardService.newEventAction()
.setActionRuleId("CEL_TEXTINPUT_FAILURE_RULE_ID")
.setCommonWidgetAction(
CardService.newCommonWidgetAction()
.setUpdateVisibilityAction(
CardService.newUpdateVisibilityAction()
.setVisibility(
CardService.Visibility.VISIBLE)));
const widgetEventActionSuccess = CardService.newEventAction()
.setActionRuleId("CEL_TEXTINPUT_SUCCESS_RULE_ID")
.setCommonWidgetAction(
CardService.newCommonWidgetAction()
.setUpdateVisibilityAction(
CardService.newUpdateVisibilityAction()
.setVisibility(
CardService.Visibility.HIDDEN)));
const errorWidget = CardService.newTextParagraph()
.setText("The first and second value must match.")
.setVisibility(CardService.Visibility.HIDDEN) // Initially hidden
.addEventAction(widgetEventActionFail)
.addEventAction(widgetEventActionSuccess);
sections = sections.addWidget(errorWidget);
card = card.addSection(sections);
// Build and return the Card
return card.build();
}
JSON 매니페스트 파일
{
"timeZone": "America/Los_Angeles",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"addOns": {
"common": {
"name": "CEL validation example",
"logoUrl": "https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png",
"useLocaleFromApp": true
},
"flows": {
"workflowElements": [
{
"id": "actionElement",
"state": "ACTIVE",
"name": "CEL Demo",
"description": "Demonstrates CEL Validation",
"workflowAction": {
"inputs": [
{
"id": "value1",
"description": "The first number",
"cardinality": "SINGLE",
"dataType": {
"basicType": "INTEGER"
}
},
{
"id": "value2",
"description": "The second number",
"cardinality": "SINGLE",
"dataType": {
"basicType": "INTEGER"
}
}
],
"onConfigFunction": "onConfig",
"onExecuteFunction": "onExecute"
}
}
]
}
}
}
지원되는 CEL 검증 위젯 및 작업
CEL 검증을 지원하는 카드 위젯
다음 위젯은 CEL 검증을 지원합니다.
TextInputSelectionInputDateTimePicker
지원되는 CEL 검증 작업
- 산술 연산
+: 두 개의int64,uint64또는double숫자를 더합니다.-: 두 개의int64,uint64또는double숫자를 뺍니다.*: 두 개의int64,uint64또는double숫자를 곱합니다./: 두 개의int64,uint64또는double숫자를 나눕니다 (정수 나눗셈).%: 두int64또는uint64숫자의 모듈로를 계산합니다.-:int64또는uint64숫자를 부정합니다.
- 논리 연산:
&&: 두 불리언 값에 논리AND연산을 수행합니다.||: 두 불리언 값에 논리OR연산을 수행합니다.!: 불리언 값에 논리NOT연산을 수행합니다.
- 비교 작업:
==: 두 값이 같은지 확인합니다. 숫자와 목록을 지원합니다.!=: 두 값이 같지 않은지 확인합니다. 숫자와 목록을 지원합니다.<: 첫 번째int64,uint64또는double숫자가 두 번째 숫자보다 작은지 확인합니다.<=: 첫 번째int64,uint64또는double숫자가 두 번째 숫자보다 작거나 같은지 확인합니다.>: 첫 번째int64,uint64또는double숫자가 두 번째 숫자보다 큰지 확인합니다.>=: 첫 번째int64,uint64또는double숫자가 두 번째 숫자보다 크거나 같은지 확인합니다.
- 작업 나열:
in: 값이 목록에 있는지 확인합니다. 숫자, 문자열, 중첩 목록을 지원합니다.size: 목록의 항목 수를 반환합니다. 숫자와 중첩 목록을 지원합니다.
지원되지 않는 CEL 검증 시나리오
- 이진 연산의 인수 크기가 잘못됨: 이진 연산 (예:
add_int64, equals)에는 정확히 두 개의 인수가 필요합니다. 다른 수의 인수를 제공하면 오류가 발생합니다. - 단항 연산자의 잘못된 인수 크기: 단항 연산자 (예:
negate_int64)에는 인수가 하나만 필요합니다. 다른 수의 인수를 제공하면 오류가 발생합니다. - 숫자 연산에서 지원되지 않는 유형: 숫자 이항 및 단항 연산은 숫자 인수만 허용합니다. 다른 유형 (예: 불리언)을 제공하면 오류가 발생합니다.
서버 측 유효성 검사
서버 측 유효성 검사를 사용하면 단계의 코드에서 onSaveFunction()를 지정하여 서버 측 로직을 실행할 수 있습니다. 사용자가 단계의 구성 카드에서 벗어나면 onSaveFunction()가 실행되어 사용자의 입력을 확인할 수 있습니다.
사용자의 입력이 유효하면 saveWorkflowAction를 반환합니다.
사용자의 입력이 잘못된 경우 오류를 해결하는 방법을 설명하는 오류 메시지를 사용자에게 표시하는 구성 카드를 반환합니다.
서버 측 검증은 비동기식이므로 사용자가 흐름을 게시할 때까지 입력 오류를 알지 못할 수 있습니다.
매니페스트 파일의 각 검증된 입력의 id는 코드의 카드 위젯 name와 일치해야 합니다.
다음 예에서는 사용자 텍스트 입력에 '@' 기호가 포함되어 있는지 확인합니다.
매니페스트 파일
매니페스트 파일 발췌문은 'onSave'라는 onSaveFunction()를 지정합니다.
JSON
{
"timeZone": "America/Los_Angeles",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"addOns": {
"common": {
"name": "Server-side validation example",
"logoUrl": "https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png",
"useLocaleFromApp": true
},
"flows": {
"workflowElements": [
{
"id": "actionElement",
"state": "ACTIVE",
"name": "Calculate",
"description": "Asks the user for an email address",
"workflowAction": {
"inputs": [
{
"id": "email",
"description": "email address",
"cardinality": "SINGLE",
"required": true,
"dataType": {
"basicType": "STRING"
}
}
],
"onConfigFunction": "onConfigCalculate",
"onExecuteFunction": "onExecuteCalculate",
"onSaveFunction": "onSave"
}
}
]
}
}
}
애플리케이션 코드
이 단계의 코드에는 onSave()라는 함수가 포함되어 있습니다. 사용자가 입력한 문자열에 @가 포함되어 있는지 확인합니다. 포함되어 있으면 흐름 단계를 저장합니다. 그렇지 않으면 오류를 수정하는 방법을 설명하는 오류 메시지가 포함된 구성 카드를 반환합니다.
Apps Script
/**
* Validates user input asynchronously when the user
* navigates away from a step's configuration card.
*/
function onSave(event) {
// "email" matches the input ID specified in the manifest file.
var email = event.workflow.actionInvocation.inputs["email"];
// Validate that the email address contains an "@" sign:
if(email.includes("@")) {
// If successfully validated, save and proceed.
return {
"hostAppAction" : {
"workflowAction" : {
"saveWorkflowAction" : {}
}
}
};
// If the input is invalid, return a card with an error message
} else {
var card = {
"sections": [
{
"header": "Collect Email",
"widgets": [
{
"textInput": {
"name": "email",
"label": "email address",
"hostAppDataSource" : {
"workflowDataSource" : {
"includeVariables" : true
}
}
}
},
{
"textParagraph": {
"text": "<b>Error:</b> Email addresses must include the '@' sign.",
"maxLines": 1
}
}
]
}
]
};
return pushCard(card);
}
}