이 가이드에서는 입력 변수를 검증하는 방법을 설명합니다.
입력 변수를 정의할 때는 사용자가 적절한 값을 입력하는지 확인하는 것이 좋습니다. 예를 들어 사용자에게 숫자를 입력하라고 요청하는 경우 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 검증을 지원합니다.
- TextInput
- SelectionInput
- DateTimePicker
지원되는 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);
  }
}