Pub/Sub를 사용하는 Google Chat 앱 빌드

이 페이지에서는 Cloud Pub/Sub를 사용하여 Chat에서 이벤트를 수신하는 Chat 앱을 만드는 방법을 설명합니다. 이 아키텍처는 Chat 앱이 방화벽 뒤에 있거나 Google Workspace Events API를 사용하여 Chat 스페이스 또는 사용자에 관한 이벤트를 보내거나 받으려는 경우에 유용합니다.

다음 다이어그램은 Pub/Sub로 빌드된 채팅 앱의 아키텍처를 보여줍니다.

Pub/Sub로 구현된 Chat 앱의 아키텍처

위 다이어그램에서 Pub/Sub 채팅 앱과 상호작용하는 사용자의 정보 흐름은 다음과 같습니다.

  1. 사용자가 메시지를 보내거나, 명령어를 실행하거나, Chat 스페이스에서 추가 또는 삭제하는 등 Chat 앱과 상호작용합니다.

  2. Chat에서 메시지를 Pub/Sub 주제로 전송합니다.

  3. Chat 앱 로직이 포함된 클라우드 또는 온프레미스 시스템인 애플리케이션 서버는 방화벽을 통해 메시지를 수신하기 위해 Pub/Sub 주제를 구독합니다.

  4. 선택적으로 Chat 앱은 Chat API를 호출하여 메시지를 비동기식으로 게시하거나 다른 작업을 실행할 수 있습니다.

기본 요건

Node.js

  • Google Chat에 액세스할 수 있는 Business 또는 Enterprise Google Workspace 계정
  • 결제가 사용 설정된 Google Cloud 프로젝트. 기존 프로젝트에 결제가 사용 설정되어 있는지 확인하려면 프로젝트의 결제 상태 확인을 참고하세요. 프로젝트를 만들고 청구를 설정하려면 Google Cloud 프로젝트 만들기를 참고하세요.
  • Node.js 14 이상
  • npm 패키지 관리 도구
  • 초기화된 Node.js 프로젝트 새 프로젝트를 초기화하려면 새 폴더를 만들고 해당 폴더로 전환한 다음 명령줄 인터페이스에서 다음 명령어를 실행합니다.
    npm init

Python

자바

API 사용 설정

Google API를 사용하려면 먼저 Google Cloud 프로젝트에서 사용 설정해야 합니다. 단일 Google Cloud 프로젝트에서 하나 이상의 API를 사용 설정할 수 있습니다.
  • Google Cloud 콘솔에서 Google Chat API와 Pub/Sub API를 사용 설정합니다.

    API 사용 설정

Pub/Sub 설정

  1. Chat API가 메시지를 보낼 수 있는 Pub/Sub 주제를 만듭니다. Chat 앱당 하나의 주제를 사용하는 것이 좋습니다.

  2. Chat 앱이 Pub/Sub 및 Chat으로 승인할 수 있도록 서비스 계정을 만들고 비공개 키 파일을 작업 디렉터리에 저장합니다.

  3. 주제에 대한 풀 구독을 만듭니다.

  4. 이전에 만든 서비스 계정에 대해 구독에 Pub/Sub 구독자 역할을 할당합니다.

스크립트 작성

이 섹션에서는 Chat 앱의 애플리케이션 로직을 정의합니다. Google Cloud로 인증하고 Pub/Sub 주제를 구독하여 사용자가 Chat 앱에 메시지를 보낼 때와 같은 Chat의 이벤트를 수신하는 스크립트를 작성합니다.

스크립트는 메시지를 수신하면 이벤트 데이터를 처리하고 Google Chat API를 사용하여 사용자 또는 스페이스에 응답을 다시 게시합니다. 이 설정을 사용하면 Chat 앱이 방화벽 뒤에서 작동하면서도 Chat 사용자와 상호작용할 수 있습니다.

Node.js

  1. CLI에서 서비스 계정 사용자 인증 정보를 제공합니다.

    export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
    
  2. CLI에서 Google Cloud 프로젝트 ID를 제공합니다.

    export PROJECT_ID=PROJECT_ID
    
  3. CLI에서 이전에 만든 Pub/Sub 구독의 구독 ID를 제공합니다.

    export SUBSCRIPTION_ID=SUBSCRIPTION_ID
    
  4. 작업 디렉터리에 package.json라는 파일을 만듭니다.

  5. package.json 파일에 다음 코드를 붙여넣습니다.

    {
      "name": "pub-sub-app",
      "version": "1.0.0",
      "description": "Google Chat App that listens for messages via Cloud Pub/Sub",
      "main": "index.js",
      "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "dependencies": {
        "@google-apps/chat": "^0.4.0",
        "@google-cloud/pubsub": "^4.5.0"
      },
      "license": "Apache-2.0"
    }
    
  6. 작업 디렉터리에 index.js라는 파일을 만듭니다.

  7. index.js에 다음 코드를 붙여넣습니다.

    const {ChatServiceClient} = require('@google-apps/chat');
    const {MessageReplyOption} = require('@google-apps/chat').protos.google.chat.v1.CreateMessageRequest;
    const {PubSub} = require('@google-cloud/pubsub');
    const {SubscriberClient} = require('@google-cloud/pubsub/build/src/v1');
    
    // Receives messages from a pull subscription.
    function receiveMessages() {
      const chat = new ChatServiceClient({
        keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS,
        scopes: ['https://www.googleapis.com/auth/chat.bot'],
      });
    
      const subscriptionPath = new SubscriberClient()
        .subscriptionPath(process.env.PROJECT_ID, process.env.SUBSCRIPTION_ID)
      const subscription = new PubSub()
        .subscription(subscriptionPath);
    
      // Handle incoming message, then acknowledge the received message
      const messageHandler = message => {
        console.log(`Id : ${message.id}`);
        const event = JSON.parse(message.data);
        console.log(`Data : ${JSON.stringify(event)}`);
    
        // Post the response to Google Chat.
        const request = formatRequest(event);
        if (request != null) {
          chat.createMessage(request);
        }
    
        // Acknowledge the message.
        message.ack();
      }
    
      subscription.on('message', messageHandler);
      console.log(`Listening for messages on ${subscriptionPath}`);
    
      // Keep main thread from exiting while waiting for messages
      setTimeout(() => {
        subscription.removeListener('message', messageHandler);
        console.log(`Stopped listening for messages.`);
      }, 60 * 1000);
    }
    
    // Send message to Google Chat based on the type of event
    function formatRequest(event) {
      const chatEvent = event.chat || {};
    
      // If the app was removed, we don't respond.
      if (chatEvent.removedFromSpacePayload) {
        console.log(`App removed from space.`);
        return null;
      }
    
      const payload = chatEvent.messagePayload || chatEvent.addedToSpacePayload;
      const spaceName = payload?.space?.name;
    
      if (!spaceName) {
        console.log('No space name in event.');
        return null;
      }
    
      if (chatEvent.addedToSpacePayload) {
        // An app can also be added to a space by @mentioning it in a
        // message. In that case, we fall through to the message case
        // and let the app respond. If the app was added using the
        // invite flow, we just post a thank you message in the space.
        return {
          parent: spaceName,
          message: { text: 'Thank you for adding me!' },
        };
      } else if (chatEvent.messagePayload) {
        // In case of message, post the response in the same thread.
        const message = chatEvent.messagePayload.message;
        return {
          parent: spaceName,
          messageReplyOption: MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD,
          message: {
            text: 'You said: `' + message.text + '`',
            thread: { name: message.thread.name },
          },
        };
      }
    }
    
    if (!process.env.PROJECT_ID) {
      console.log('Missing PROJECT_ID env var.');
      process.exit(1);
    }
    if (!process.env.SUBSCRIPTION_ID) {
      console.log('Missing SUBSCRIPTION_ID env var.');
      process.exit(1);
    }
    if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {
      console.log('Missing GOOGLE_APPLICATION_CREDENTIALS env var.');
      process.exit(1);
    }
    
    receiveMessages();
    

Python

  1. CLI에서 서비스 계정 사용자 인증 정보를 제공합니다.

    export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
    
  2. CLI에서 Google Cloud 프로젝트 ID를 제공합니다.

    export PROJECT_ID=PROJECT_ID
    
  3. CLI에서 이전에 만든 Pub/Sub 구독의 구독 ID를 제공합니다.

    export SUBSCRIPTION_ID=SUBSCRIPTION_ID
    
  4. 작업 디렉터리에 requirements.txt라는 파일을 만듭니다.

  5. requirements.txt 파일에 다음 코드를 붙여넣습니다.

    google-cloud-pubsub>=2.23.0
    google-apps-chat==0.1.9
    
  6. 작업 디렉터리에 app.py라는 파일을 만듭니다.

  7. app.py에 다음 코드를 붙여넣습니다.

    import json
    import logging
    import os
    import sys
    import time
    from google.apps import chat_v1 as google_chat
    from google.cloud import pubsub_v1
    from google.oauth2.service_account import Credentials
    
    def receive_messages():
      """Receives messages from a pull subscription."""
    
      scopes = ['https://www.googleapis.com/auth/chat.bot']
      service_account_key_path = os.environ.get('GOOGLE_APPLICATION_CREDENTIALS')
      creds = Credentials.from_service_account_file(service_account_key_path)
      chat = google_chat.ChatServiceClient(
          credentials=creds, client_options={'scopes': scopes}
      )
    
      project_id = os.environ.get('PROJECT_ID')
      subscription_id = os.environ.get('SUBSCRIPTION_ID')
      subscriber = pubsub_v1.SubscriberClient()
      subscription_path = subscriber.subscription_path(project_id, subscription_id)
    
      # Handle incoming message, then acknowledge the received message
      def callback(message):
        event = json.loads(message.data)
        logging.info('Data : %s', event)
    
        # Post the response to Google Chat.
        request = format_request(event)
        if request is not None:
          chat.create_message(request)
    
        # Acknowledge the message.
        message.ack()
    
      subscriber.subscribe(subscription_path, callback = callback)
      logging.info('Listening for messages on %s', subscription_path)
    
      # Keep main thread from exiting while waiting for messages
      while True:
        time.sleep(60)
    
    def format_request(event):
      """Send message to Google Chat based on the type of event.
      Args:
        event: A dictionary with the event data.
      """
      chat_event = event.get('chat', {})
    
      # If the app was removed, we don't respond.
      if 'removedFromSpacePayload' in chat_event:
        logging.info('App removed from space.')
        return
    
      payload = chat_event.get('messagePayload') or chat_event.get(
          'addedToSpacePayload'
      )
      space_name = payload.get('space', {}).get('name') if payload else None
    
      if not space_name:
        logging.warning('No space name in event.')
        return
    
      if 'addedToSpacePayload' in chat_event:
        # An app can also be added to a space by @mentioning it in a
        # message. In that case, we fall through to the message case
        # and let the app respond. If the app was added using the
        # invite flow, we just post a thank you message in the space.
        return google_chat.CreateMessageRequest(
            parent = space_name,
            message = {
              'text': 'Thank you for adding me!'
            }
        )
      elif 'messagePayload' in chat_event:
        # In case of message, post the response in the same thread.
        message = chat_event['messagePayload']['message']
        return google_chat.CreateMessageRequest(
            parent = space_name,
            message_reply_option = google_chat.CreateMessageRequest.MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD,
            message = {
              'text': 'You said: `' + message['text'] + '`',
              'thread': {
                'name': message['thread']['name']
              }
            }
        )
    
    if __name__ == '__main__':
      if 'PROJECT_ID' not in os.environ:
        logging.error('Missing PROJECT_ID env var.')
        sys.exit(1)
    
      if 'SUBSCRIPTION_ID' not in os.environ:
        logging.error('Missing SUBSCRIPTION_ID env var.')
        sys.exit(1)
    
      if 'GOOGLE_APPLICATION_CREDENTIALS' not in os.environ:
        logging.error('Missing GOOGLE_APPLICATION_CREDENTIALS env var.')
        sys.exit(1)
    
      logging.basicConfig(
          level=logging.INFO,
          style='{',
          format='{levelname:.1}{asctime} {filename}:{lineno}] {message}')
      receive_messages()
    

자바

  1. CLI에서 서비스 계정 사용자 인증 정보를 제공합니다.

    export GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH
    
  2. CLI에서 Google Cloud 프로젝트 ID를 제공합니다.

    export PROJECT_ID=PROJECT_ID
    
  3. CLI에서 이전에 만든 Pub/Sub 구독의 구독 ID를 제공합니다.

    export SUBSCRIPTION_ID=SUBSCRIPTION_ID
    
  4. 작업 디렉터리에 pom.xml라는 파일을 만듭니다.

  5. pom.xml 파일에 다음 코드를 붙여넣습니다.

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
    
     <groupId>com.google.chat.addon</groupId>
     <artifactId>pubsub-addon-chat-app</artifactId>
     <version>0.1.0</version>
    
     <name>pubsub-addon-chat-app-java</name>
    
     <properties>
       <maven.compiler.release>11</maven.compiler.release>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
    
     <dependencyManagement>
       <dependencies>
         <dependency>
           <groupId>com.google.cloud</groupId>
           <artifactId>libraries-bom</artifactId>
           <version>26.41.0</version> <!-- Use a recent BOM version -->
           <type>pom</type>
           <scope>import</scope>
         </dependency>
       </dependencies>
     </dependencyManagement>
    
     <dependencies>
       <!-- Google Chat GAPIC library -->
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-chat</artifactId>
       </dependency>
       <!-- Google Cloud Pub/Sub library -->
       <dependency>
         <groupId>com.google.cloud</groupId>
         <artifactId>google-cloud-pubsub</artifactId>
       </dependency>
       <!-- Google Apps Add-ons Event Object -->
       <dependency>
         <groupId>com.google.apps.addons.v1</groupId>
         <artifactId>google-apps-addons-v1-java</artifactId>
         <version>0.2.0</version> <!-- Check for latest version -->
       </dependency>
       <!-- Protobuf JSON utility -->
       <dependency>
         <groupId>com.google.protobuf</groupId>
         <artifactId>protobuf-java-util</artifactId>
       </dependency>
       <!-- Google Auth Library -->
       <dependency>
         <groupId>com.google.auth</groupId>
         <artifactId>google-auth-library-oauth2-http</artifactId>
       </dependency>
       <dependency>
         <groupId>com.google.api</groupId>
         <artifactId>gax</artifactId>
       </dependency>
       <!-- JSON utilities for PubSub message (if needed, though protobuf-java-util is primary for EventObject) -->
       <dependency>
         <groupId>com.fasterxml.jackson.core</groupId>
         <artifactId>jackson-databind</artifactId>
         <version>2.14.2</version>
       </dependency>
       <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-jdk14</artifactId>
         <version>1.7.36</version>
         <scope>runtime</scope>
       </dependency>
     </dependencies>
    
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.13.0</version>
           <configuration>
             <source>11</source>
             <target>11</target>
           </configuration>
         </plugin>
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>exec-maven-plugin</artifactId>
           <version>3.3.0</version>
           <configuration>
             <mainClass>Main</mainClass>
           </configuration>
         </plugin>
       </plugins>
     </build>
    </project>
    
  6. 작업 디렉터리에서 src/main/java 디렉터리 구조를 만듭니다.

  7. src/main/java 디렉터리에 Main.java이라는 파일을 만듭니다.

  8. Main.java에 다음 코드를 붙여넣습니다.

    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.api.gax.core.FixedCredentialsProvider;
    import com.google.auth.oauth2.GoogleCredentials;
    import com.google.chat.v1.ChatServiceClient;
    import com.google.chat.v1.ChatServiceSettings;
    import com.google.chat.v1.CreateMessageRequest;
    import com.google.chat.v1.CreateMessageRequest.MessageReplyOption;
    import com.google.chat.v1.Message;
    import com.google.chat.v1.Thread;
    import com.google.cloud.pubsub.v1.AckReplyConsumer;
    import com.google.cloud.pubsub.v1.MessageReceiver;
    import com.google.cloud.pubsub.v1.Subscriber;
    import com.google.pubsub.v1.ProjectSubscriptionName;
    import com.google.pubsub.v1.PubsubMessage;
    import java.io.FileInputStream;
    import java.util.Collections;
    
    public class Main {
    
      public static final String PROJECT_ID_ENV_PROPERTY = "PROJECT_ID";
      public static final String SUBSCRIPTION_ID_ENV_PROPERTY = "SUBSCRIPTION_ID";
      public static final String CREDENTIALS_PATH_ENV_PROPERTY = "GOOGLE_APPLICATION_CREDENTIALS";
    
      public static void main(String[] args) throws Exception {
        ProjectSubscriptionName subscriptionName =
            ProjectSubscriptionName.of(
                System.getenv(Main.PROJECT_ID_ENV_PROPERTY),
                System.getenv(Main.SUBSCRIPTION_ID_ENV_PROPERTY));
    
        // Instantiate app, which implements an asynchronous message receiver.
        EchoApp echoApp = new EchoApp();
    
        // Create a subscriber for <var>SUBSCRIPTION_ID</var> bound to the message receiver
        final Subscriber subscriber = Subscriber.newBuilder(subscriptionName, echoApp).build();
        System.out.println("Subscriber is listening to events...");
        subscriber.startAsync();
    
        // Wait for termination
        subscriber.awaitTerminated();
      }
    }
    
    /**
     * A demo app which implements {@link MessageReceiver} to receive messages.
     * It echoes incoming messages.
     */
    class EchoApp implements MessageReceiver {
    
      // Path to the private key JSON file of the service account to be used for posting response
      // messages to Google Chat.
      // In this demo, we are using the same service account for authorizing with Cloud Pub/Sub to
      // receive messages and authorizing with Google Chat to post messages. If you are using
      // different service accounts, set the path to the private key JSON file of the service
      // account used to post messages to Google Chat here.
      private static final String SERVICE_ACCOUNT_KEY_PATH =
        System.getenv(Main.CREDENTIALS_PATH_ENV_PROPERTY);
    
      // Developer code for Google Chat API scope.
      private static final String GOOGLE_CHAT_API_SCOPE = "https://www.googleapis.com/auth/chat.bot";
    
      private static final String ADDED_RESPONSE = "Thank you for adding me!";
    
      ChatServiceClient chatServiceClient;
    
      EchoApp() throws Exception {
        GoogleCredentials credential =
            GoogleCredentials.fromStream(new FileInputStream(SERVICE_ACCOUNT_KEY_PATH))
                .createScoped(Collections.singleton(GOOGLE_CHAT_API_SCOPE));
    
        // Create the ChatServiceSettings with the app credentials
        ChatServiceSettings chatServiceSettings =
            ChatServiceSettings.newBuilder()
                .setCredentialsProvider(FixedCredentialsProvider.create(credential))
                .build();
    
        // Set the Chat service client
        chatServiceClient = ChatServiceClient.create(chatServiceSettings);
      }
    
      // Called when a message is received by the subscriber.
      @Override
      public void receiveMessage(PubsubMessage pubsubMessage, AckReplyConsumer consumer) {
        System.out.println("Id : " + pubsubMessage.getMessageId());
        // Handle incoming message, then acknowledge the received message
        try {
          ObjectMapper mapper = new ObjectMapper();
          JsonNode dataJson = mapper.readTree(pubsubMessage.getData().toStringUtf8());
          System.out.println("Data : " + dataJson.toString());
          handle(dataJson);
          consumer.ack();
        } catch (Exception e) {
          System.out.println(e);
          // Negative acknowledgement makes Pub/Sub redeliver the message.
          consumer.nack();
        }
      }
    
      // Send message to Google Chat based on the type of event.
      public void handle(JsonNode eventJson) throws Exception {
        // Google Chat events for add-ons are wrapped in a 'chat' object.
        if (!eventJson.has("chat")) {
          System.out.println("Ignored: Not a Chat event (missing 'chat' field).");
          return;
        }
    
        JsonNode chatNode = eventJson.get("chat");
        CreateMessageRequest createMessageRequest = null;
    
        if (chatNode.has("messagePayload")) {
          // HANDLE MESSAGE
          JsonNode messagePayload = chatNode.get("messagePayload");
          JsonNode message = messagePayload.get("message");
          JsonNode space = messagePayload.get("space");
    
          String spaceName = space.get("name").asText();
          String userText = message.has("text") ? message.get("text").asText() : "";
          String threadName = message.has("thread") ? message.get("thread").get("name").asText() : "";
    
          System.out.println("Received message in " + spaceName + ": " + userText);
    
          createMessageRequest =
              CreateMessageRequest.newBuilder()
                  .setParent(spaceName)
                  .setMessageReplyOption(MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD)
                  .setMessage(
                      Message.newBuilder()
                          .setText("You said: `" + userText + "`")
                          .setThread(Thread.newBuilder().setName(threadName).build())
                          .build())
                  .build();
    
        } else if (chatNode.has("addedToSpacePayload")) {
          // HANDLE ADDED TO SPACE
          JsonNode addedPayload = chatNode.get("addedToSpacePayload");
          JsonNode space = addedPayload.get("space");
          String spaceName = space.get("name").asText();
    
          System.out.println("Added to space: " + spaceName);
    
          createMessageRequest =
              CreateMessageRequest.newBuilder()
                  .setParent(spaceName)
                  .setMessage(Message.newBuilder().setText(ADDED_RESPONSE).build())
                  .build();
    
        } else if (chatNode.has("removedFromSpacePayload")) {
          System.out.println("Removed from space.");
          return;
        } else {
          System.out.println("Ignored: Unhandled Chat event type.");
          return;
        }
    
        if (createMessageRequest != null) {
          // Post the response to Google Chat.
          chatServiceClient.createMessage(createMessageRequest);
          System.out.println("Sent reply.");
        }
      }
    }
    

Chat 앱 구성

Google Cloud 콘솔에서 Chat 앱을 구성하여 이름, 아바타와 같은 세부정보를 제공하고 Pub/Sub 주제에 대한 연결을 설정합니다.

Pub/Sub 주제에 연결하면 Chat에서 앱에 이벤트를 보낼 수 있습니다. 주제를 구독하는 스크립트는 이러한 이벤트를 수신하고 사용자에게 응답할 수 있습니다.

  1. Google Cloud 콘솔에서 메뉴 > API 및 서비스 > 사용 설정된 API 및 서비스 > Google Chat API > 구성으로 이동합니다.

    Chat API 구성으로 이동

  2. Pub/Sub용 Chat 앱을 구성합니다.

    1. 앱 이름Add-on Chat App를 입력합니다.
    2. 아바타 URLhttps://developers.google.com/workspace/add-ons/images/quickstart-app-avatar.png를 입력합니다.
    3. 설명Quickstart app을 입력합니다.
    4. 기능에서 스페이스 및 그룹 대화 참여를 선택합니다.
    5. 연결 설정에서 Cloud Pub/Sub을 선택하고 이전에 만든 Pub/Sub 주제의 이름을 붙여넣습니다.
    6. 공개 상태에서 도메인의 특정 사용자 및 그룹에서 이 Google Chat 앱을 사용할 수 있도록 설정을 선택하고 이메일 주소를 입력합니다.
    7. 로그에서 Logging에 오류 로깅을 선택합니다.
  3. 저장을 클릭합니다.

이제 Chat 앱을 구성했으므로 Pub/Sub 구성을 업데이트해야 합니다.

  1. Chat API 구성 페이지연결 설정에서 Google Cloud 프로젝트에 대해 생성된 고유한 이메일인 서비스 계정 이메일을 복사합니다.
  2. 이전에 복사한 서비스 계정 이메일에 Pub/Sub 게시자 역할을 할당하여 주제에 게시할 수 있는 채팅 권한을 부여합니다.

앱에서 Chat의 메시지를 수신하고 응답할 수 있습니다.

스크립트 실행

CLI에서 작업 디렉터리로 전환하고 스크립트를 실행합니다.

Node.js

npm install
npm start

Python

python -m venv env
source env/bin/activate
pip install -r requirements.txt -U
python app.py

자바

mvn compile exec:java -Dexec.mainClass=Main

코드를 실행하면 애플리케이션이 Pub/Sub 주제에 게시된 메시지 수신 대기를 시작합니다.

Chat 앱 테스트

Chat 앱을 테스트하려면 Chat 앱과의 채팅 메시지 스페이스를 열고 메시지를 보냅니다.

  1. 신뢰할 수 있는 테스터로 자신을 추가할 때 제공한 Google Workspace 계정을 사용하여 Google Chat을 엽니다.

    Google Chat으로 이동

  2. 새 채팅을 클릭합니다.
  3. 사용자 1명 이상 추가 필드에 Chat 앱의 이름을 입력합니다.
  4. 검색 결과에서 채팅 앱을 선택합니다. 채팅 메시지가 열립니다.

  5. 앱의 새 채팅 메시지에 Hello를 입력하고 enter를 누릅니다.

신뢰할 수 있는 테스터를 추가하고 대화형 기능 테스트에 대해 자세히 알아보려면 Google Chat 앱의 대화형 기능 테스트를 참고하세요.

문제 해결

Google Chat 앱 또는 카드에서 오류를 반환하면 Chat 인터페이스에 '문제가 발생했습니다'라는 메시지가 표시됩니다. 또는 '요청을 처리할 수 없습니다' Chat UI에 오류 메시지가 표시되지 않지만 Chat 앱이나 카드에서 예상치 못한 결과가 발생하는 경우가 있습니다. 예를 들어 카드 메시지가 표시되지 않을 수 있습니다.

채팅 UI에 오류 메시지가 표시되지 않을 수도 있지만, 채팅 앱의 오류 로깅이 사용 설정된 경우 오류를 수정하는 데 도움이 되는 설명 오류 메시지와 로그 데이터를 사용할 수 있습니다. 오류를 확인하고, 디버그하고, 수정하는 데 도움이 필요하면 Google Chat 오류 문제 해결 및 수정을 참고하세요.

삭제

이 튜토리얼에서 사용한 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 Cloud 프로젝트를 삭제하는 것이 좋습니다.

  1. Google Cloud 콘솔에서 리소스 관리 페이지로 이동합니다. 메뉴 > IAM 및 관리자 > 리소스 관리를 클릭합니다.

    Resource Manager로 이동

  2. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제 를 클릭합니다.
  3. 대화상자에서 프로젝트 ID를 입력한 다음 종료를 클릭하여 프로젝트를 삭제합니다.