입력 변수 유효성 검사

이 가이드에서는 입력 변수를 검증하는 방법을 설명합니다.

입력 변수를 정의할 때는 사용자가 적절한 값을 입력하는지 확인하는 것이 좋습니다. 예를 들어 사용자에게 숫자를 입력하라고 요청하는 경우 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: 카드 측 TriggeractionRuleId와 일치합니다.
    • CommonWidgetAction: 위젯 표시 상태 업데이트와 같이 평가가 포함되지 않는 작업을 정의합니다.
      • UpdateVisibilityAction: 위젯의 표시 상태 (VISIBLE 또는 HIDDEN)를 업데이트하는 작업입니다.

다음 예에서는 두 텍스트 입력이 동일한지 확인하기 위해 CEL 유효성 검사를 구현하는 방법을 보여줍니다. 같지 않으면 오류 메시지가 표시됩니다.

  • failCondition이 충족되면 (입력이 같지 않음) 오류 메시지 위젯이 VISIBLE로 설정되고 표시됩니다.
    그림 1: failCondition이 충족되면 (입력이 같지 않음) 오류 메시지 위젯이 VISIBLE로 설정되고 표시됩니다.
  • successCondition이 충족되면 (입력이 동일함) 오류 메시지 위젯이 HIDDEN으로 설정되어 표시되지 않습니다.
    그림 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);
  }
}