HTTP Google Chat 앱 빌드

이 페이지에서는 HTTP Chat 앱을 만드는 방법을 설명합니다. 이 아키텍처를 구현하는 방법은 여러 가지가 있습니다. Google Cloud에서는 Cloud Run 및 App Engine을 사용할 수 있습니다. 이 빠른 시작에서는 Chat 앱이 사용자의 메시지에 응답하는 데 사용하는 Cloud Run 함수를 작성하고 배포합니다.

이 아키텍처를 사용하면 다음 다이어그램과 같이 HTTP를 사용하여 Google Cloud 또는 온프레미스 서버와 통합되도록 Chat을 구성합니다.

온프레미스 서버에서 웹 서비스를 사용하는 Chat 앱의 아키텍처

위의 다이어그램에서 HTTP Chat 앱과 상호작용하는 사용자는 다음과 같은 정보 흐름을 갖습니다.

  1. 사용자가 채팅 메시지 또는 Chat 스페이스에서 Chat 앱에 메시지를 보냅니다.
  2. HTTP 요청이 Chat 앱 로직이 포함된 클라우드 또는 온프레미스 시스템인 웹 서버로 전송됩니다.
  3. 선택적으로 Chat 앱 로직은 Google Workspace 서비스 (예: Calendar 및 Sheets), 기타 Google 서비스(예: 지도, YouTube, Vertex AI) 또는 기타 웹 서비스 (예: 프로젝트 관리 시스템 또는 티켓팅 도구)와 통합될 수 있습니다.
  4. 웹 서버가 HTTP 응답을 Chat의 Chat 앱 서비스로 다시 전송합니다.
  5. 응답이 사용자에게 전달됩니다.
  6. 선택적으로 Chat 앱은 Chat API를 호출하여 메시지를 비동기식으로 게시하거나 다른 작업을 실행할 수 있습니다.

이 아키텍처는 다양한 프로그래밍 언어를 사용하여 이러한 Chat 앱을 설계할 수 있으므로 시스템에 이미 있는 기존 라이브러리 및 구성요소를 유연하게 사용할 수 있습니다.

목표

  • 환경을 설정합니다.
  • Cloud Run 함수를 만들고 배포합니다.
  • Chat에 앱을 게시합니다.
  • 앱을 테스트합니다.

기본 요건

환경 설정

Google API를 사용하기 전에 Google Cloud 프로젝트에서 API를 사용 설정해야 합니다. 단일 Google Cloud 프로젝트에서 하나 이상의 API를 사용 설정할 수 있습니다.
  • Google API 콘솔에서 Google Chat API, Cloud Build API, Cloud Functions API, Cloud Pub/Sub API, Cloud Logging API, Artifact Registry API, Cloud Run API를 사용 설정합니다.

    API 사용 설정

Cloud Run 함수 만들기 및 배포

보낸사람의 표시 이름과 아바타 이미지가 포함된 Chat 카드를 생성하는 Cloud Run 함수를 만들고 배포합니다. Chat 앱이 메시지를 수신하면 함수를 실행하고 카드로 응답합니다.

Chat 앱의 함수를 만들고 배포하려면 다음 단계를 완료하세요.

Node.js

  1. Google API 콘솔에서 Cloud Run 페이지로 이동합니다.

    Cloud Run으로 이동

    Chat 앱의 프로젝트가 선택되어 있는지 확인합니다.

  2. 함수 작성 을 클릭합니다.

  3. 서비스 만들기 페이지에서 함수를 설정합니다.

    1. 서비스 이름 필드에 quickstartchatapp을 입력합니다.
    2. 리전 목록에서 리전을 선택합니다.
    3. 런타임 목록에서 최신 버전의 Node.js를 선택합니다.
    4. 인증 섹션에서 인증 필요 를 선택합니다.
    5. 만들기를 클릭하고 Cloud Run이 서비스를 만들 때까지 기다립니다. 콘솔에서 소스 탭으로 리디렉션됩니다.
  4. 소스 탭에서 다음을 실행합니다.

    1. 진입점에서 기본 텍스트를 삭제하고 avatarApp을 입력합니다.
    2. index.js의 내용을 다음 코드로 바꿉니다.

      node/avatar-app/index.js
      const functions = require('@google-cloud/functions-framework');
      
      // Command IDs (configure these in Google Chat API)
      const ABOUT_COMMAND_ID = 1; // ID for the "/about" slash command
      const HELP_COMMAND_ID = 2; // ID for the "Help" quick command
      
      /**
       * Google Cloud Function that handles HTTP requests from Google Chat.
       *
       * @param {Object} req - The HTTP request object sent from Google Chat.
       * @param {Object} res - The HTTP response object.
       */
      functions.http('avatarApp', (req, res) => {
        const event = req.body;
      
        if (event.appCommandMetadata) {
          handleAppCommands(event, res);
        } else {
          handleRegularMessage(event, res);
        }
      });
      
      /**
       * Handles slash and quick commands.
       *
       * @param {Object} event - The Google Chat event.
       * @param {Object} res - The HTTP response object.
       */
      function handleAppCommands(event, res) {
        const {appCommandId, appCommandType} = event.appCommandMetadata;
      
        switch (appCommandId) {
          case ABOUT_COMMAND_ID:
            return res.send({
              privateMessageViewer: event.user,
              text: 'The Avatar app replies to Google Chat messages.'
            });
          case HELP_COMMAND_ID:
            return res.send({
              privateMessageViewer: event.user,
              text: 'The Avatar app replies to Google Chat messages.'
            });
        }
      }
      
      /**
       * Handles regular messages (not commands).
       *
       * @param {Object} event - The Google Chat event.
       * @param {Object} res - The HTTP response object.
       */
      function handleRegularMessage(event, res) {
        const messageData = createMessage(event.user);
        res.send(messageData);
      }
      
      /**
       * Creates a card message with the user's avatar.
       *
       * @param {Object} user - The user who sent the message.
       * @param {string} user.displayName - The user's display name.
       * @param {string} user.avatarUrl - The URL of the user's avatar.
       * @return {Object} - The card message object.
       */
      function createMessage({displayName, avatarUrl}) {
        return {
          text: 'Here\'s your avatar',
          cardsV2: [{
            cardId: 'avatarCard',
            card: {
              name: 'Avatar Card',
              header: {
                title: `Hello ${displayName}!`,
              },
              sections: [{
                widgets: [
                  {textParagraph: {text: 'Your avatar picture:'}},
                  {image: {imageUrl: avatarUrl}},
                ],
              }],
            },
          }],
        };
      }

    3. 저장 및 재배포 를 클릭합니다.

Python

  1. Google API 콘솔에서 Cloud Run 페이지로 이동합니다.

    Cloud Run으로 이동

    Chat 앱의 프로젝트가 선택되어 있는지 확인합니다.

  2. 함수 작성 을 클릭합니다.

  3. 서비스 만들기 페이지에서 함수를 설정합니다.

    1. 서비스 이름 필드에 quickstartchatapp을 입력합니다.
    2. 리전 목록에서 리전을 선택합니다.
    3. 런타임 목록에서 최신 버전의 Python을 선택합니다.
    4. 인증 섹션에서 인증 필요 를 선택합니다.
    5. 만들기를 클릭하고 Cloud Run이 서비스를 만들 때까지 기다립니다. 콘솔에서 소스 탭으로 리디렉션됩니다.
  4. 소스 탭에서 다음을 실행합니다.

    1. 진입점에서 기본 텍스트를 삭제하고 avatar_app을 입력합니다.
    2. main.py의 내용을 다음 코드로 바꿉니다.

      python/avatar-app/main.py
      from typing import Any, Mapping
      
      import flask
      import functions_framework
      
      # Command IDs (configure these in Google Chat API)
      ABOUT_COMMAND_ID = 1  # ID for the "/about" slash command
      HELP_COMMAND_ID = 2  # ID for the "Help" quick command
      
      
      @functions_framework.http
      def avatar_app(req: flask.Request) -> Mapping[str, Any]:
          """Google Cloud Function that handles HTTP requests from Google Chat.
      
          Args:
              flask.Request: the request
      
          Returns:
              Mapping[str, Any]: the response
          """
          event = req.get_json(silent=True)
      
          if event and "appCommandMetadata" in event:
              return handle_app_commands(event)
          else:
              return handle_regular_message(event)
      
      
      def handle_app_commands(event: Mapping[str, Any]) -> Mapping[str, Any]:
          """Handles slash and quick commands.
      
          Args:
              Mapping[str, Any] event: The Google Chat event.
      
          Returns:
              Mapping[str, Any]: the response
          """
          app_command_id = event["appCommandMetadata"]["appCommandId"]
      
          if app_command_id == ABOUT_COMMAND_ID:
              return {
                  "privateMessageViewer": event["user"],
                  "text": "The Avatar app replies to Google Chat messages.",
              }
          elif app_command_id == HELP_COMMAND_ID:
              return {
                  "privateMessageViewer": event["user"],
                  "text": "The Avatar app replies to Google Chat messages.",
              }
          return {}
      
      
      
      
      def handle_regular_message(event: Mapping[str, Any]) -> Mapping[str, Any]:
          """Handles regular messages (not commands).
      
          Args:
              Mapping[str, Any] event: The Google Chat event.
      
          Returns:
              Mapping[str, Any]: the response
          """
      
          if not event or "user" not in event:
              return "Invalid request."
      
          message_data = create_message(event["user"])
          return message_data
      
      
      def create_message(user: Mapping[str, Any]) -> Mapping[str, Any]:
          """Creates a card message with the user's avatar.
      
          Args:
              Mapping[str, Any] user: The user who sent the message.
      
          Returns:
              Mapping[str, Any]: a card with the user's avatar.
          """
          display_name = user.get("displayName", "")
          avatar_url = user.get("avatarUrl", "")
      
          return {
              "text": "Here's your avatar",
              "cardsV2": [
                  {
                      "cardId": "avatarCard",
                      "card": {
                          "name": "Avatar Card",
                          "header": {"title": f"Hello {display_name}!"},
                          "sections": [
                              {
                                  "widgets": [
                                      {"textParagraph": {"text": "Your avatar picture:"}},
                                      {"image": {"imageUrl": avatar_url}},
                                  ]
                              }
                          ],
                      },
                  }
              ],
          }

    3. 저장 및 재배포 를 클릭합니다.

자바

  1. Google API 콘솔에서 Cloud Run 페이지로 이동합니다.

    Cloud Run으로 이동

    Chat 앱의 프로젝트가 선택되어 있는지 확인합니다.

  2. 함수 작성 을 클릭합니다.

  3. 서비스 만들기 페이지에서 함수를 설정합니다.

    1. 서비스 이름 필드에 quickstartchatapp을 입력합니다.
    2. 리전 목록에서 리전을 선택합니다.
    3. 런타임 목록에서 최신 버전의 Java를 선택합니다.
    4. 인증 섹션에서 인증 필요 를 선택합니다.
    5. 만들기를 클릭하고 Cloud Run이 서비스를 만들 때까지 기다립니다. 콘솔에서 소스 탭으로 리디렉션됩니다.
  4. 소스 탭에서 다음을 실행합니다.

    1. 진입점에서 기본 텍스트를 삭제하고 App을 입력합니다.
    2. src/main/java/com/example/Example.java의 이름을 src/main/java/AvatarApp.java로 바꿉니다.
    3. AvatarApp.java의 내용을 다음 코드로 바꿉니다.

      java/avatar-app/src/main/java/AvatarApp.java
      import com.google.api.services.chat.v1.model.CardWithId;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1Card;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1CardHeader;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1Image;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1Section;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1TextParagraph;
      import com.google.api.services.chat.v1.model.GoogleAppsCardV1Widget;
      import com.google.api.services.chat.v1.model.Message;
      import com.google.api.services.chat.v1.model.User;
      import com.google.cloud.functions.HttpFunction;
      import com.google.cloud.functions.HttpRequest;
      import com.google.cloud.functions.HttpResponse;
      import com.google.gson.Gson;
      import com.google.gson.JsonObject;
      import java.util.List;
      
      public class AvatarApp implements HttpFunction {
        private static final Gson gson = new Gson();
      
        // Command IDs (configure these in Google Chat API)
        private static final int ABOUT_COMMAND_ID = 1; // ID for the "/about" slash command
        private static final int HELP_COMMAND_ID = 2; // ID for the "Help" quick command
      
        @Override
        public void service(HttpRequest request, HttpResponse response) throws Exception {
          JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);
      
          if (event.has("appCommandMetadata")) {
            handleAppCommands(event, response);
          } else {
            handleRegularMessage(event, response);
          }
        }
      
        /**
         * Handles slash and quick commands.
         *
         * @param event    The Google Chat event.
         * @param response The HTTP response object.
         */
        private void handleAppCommands(JsonObject event, HttpResponse response) throws Exception {
          int appCommandId = event.getAsJsonObject("appCommandMetadata").get("appCommandId").getAsInt();
      
          switch (appCommandId) {
            case ABOUT_COMMAND_ID:
              Message aboutMessage = new Message();
              aboutMessage.setText("The Avatar app replies to Google Chat messages.");
              aboutMessage.setPrivateMessageViewer(new User()
                  .setName(event.getAsJsonObject("user").get("name").getAsString()));
              response.getWriter().write(gson.toJson(aboutMessage));
              return;
            case HELP_COMMAND_ID:
              Message helpMessage = new Message();
              helpMessage.setText("The Avatar app replies to Google Chat messages.");
              helpMessage.setPrivateMessageViewer(new User()
                  .setName(event.getAsJsonObject("user").get("name").getAsString()));
              response.getWriter().write(gson.toJson(helpMessage));
              return;
          }
        }
      
        /**
         * Handles regular messages (not commands).
         *
         * @param event    The Google Chat event.
         * @param response The HTTP response object.
         */
        private void handleRegularMessage(JsonObject event, HttpResponse response) throws Exception {
      
          if (!event.has("user")) {
            response.getWriter().write("Invalid request.");
            return;
          }
      
          JsonObject user = event.getAsJsonObject("user");
          String displayName = user.has("displayName") ? user.get("displayName").getAsString() : "";
          String avatarUrl = user.has("avatarUrl") ? user.get("avatarUrl").getAsString() : "";
          Message message = createMessage(displayName, avatarUrl);
          response.getWriter().write(gson.toJson(message));
        }
      
        /**
         * Creates a card message with the user's avatar.
         *
         * @param displayName The user's display name.
         * @param avatarUrl   The URL of the user's avatar.
         * @return The card message object.
         */
        private Message createMessage(String displayName, String avatarUrl) {
          return new Message()
              .setText("Here's your avatar")
              .setCardsV2(List.of(new CardWithId()
                  .setCardId("avatarCard")
                  .setCard(new GoogleAppsCardV1Card()
                      .setName("Avatar Card")
                      .setHeader(new GoogleAppsCardV1CardHeader()
                          .setTitle(String.format("Hello %s!", displayName)))
                      .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(
                          new GoogleAppsCardV1Widget()
                              .setTextParagraph(new GoogleAppsCardV1TextParagraph()
                                  .setText("Your avatar picture:")),
                          new GoogleAppsCardV1Widget()
                              .setImage(new GoogleAppsCardV1Image().setImageUrl(avatarUrl)))))))));
        }
      }

    4. pom.xml의 내용을 다음 코드로 바꿉니다.

      java/avatar-app/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/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
      
        <groupId>gcfv2</groupId>
        <artifactId>avatar-app</artifactId>
        <version>0.0.1</version>
        <name>Avatar App</name>
      
        <properties>
          <maven.compiler.release>21</maven.compiler.release>
        </properties>
      
        <dependencies>
          <dependency>
            <groupId>com.google.cloud.functions</groupId>
            <artifactId>functions-framework-api</artifactId>
            <version>1.1.4</version>
          </dependency>
      
          <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
          <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.12.1</version>
          </dependency>
      
          <!-- https://mvnrepository.com/artifact/com.google.apis/google-api-services-chat -->
          <dependency>
            <groupId>com.google.apis</groupId>
            <artifactId>google-api-services-chat</artifactId>
            <version>v1-rev20250116-2.0.0</version>
          </dependency>
      
        </dependencies>
      
      </project>

    5. 저장 및 재배포 를 클릭합니다.

Google Chat에 함수 호출 권한 부여

Google Chat에 함수 호출 권한을 부여하려면 Cloud Run 호출자 역할이 있는 Google Chat 서비스 계정을 추가합니다.

  1. Google API 콘솔에서 Cloud Run 페이지로 이동합니다.

    Cloud Run으로 이동

  2. Cloud Run 서비스 목록에서 수신 함수 옆에 있는 체크박스를 선택합니다. 함수 자체를 클릭하지 마세요.

  3. 권한 을 클릭합니다. 권한 패널이 열립니다.

  4. 주 구성원 추가 를 클릭합니다.

  5. **새 주 구성원** 에서 chat@system.gserviceaccount.com을 입력합니다.

  6. 역할 선택에서 Cloud Run > Cloud Run 호출자를 선택합니다.

  7. 저장 을 클릭합니다.

Chat 앱 구성

Cloud Run 함수가 배포되면 다음 단계에 따라 Google Chat 앱으로 전환합니다.

  1. Google API 콘솔에서 Cloud Run 페이지로 이동합니다.

    Cloud Run으로 이동

    Cloud Run을 사용 설정한 프로젝트가 선택되어 있는지 확인합니다.

  2. 서비스 목록에서 quickstartchatapp 을 클릭합니다.

  3. 서비스 세부정보 페이지에서 함수의 URL 을 복사합니다.

  4. 'Google Chat API'를 검색하고 Google Chat API를 클릭한 후 관리를 클릭합니다.

    Chat API로 이동

  5. 구성 을 클릭하고 Google Chat 앱을 설정합니다.

    1. 이 Chat 앱을 Google Workspace 부가기능으로 빌드 를 선택 해제합니다. 확인하라는 대화상자가 열립니다. 대화상자에서 사용 중지 를 클릭합니다.
    2. 앱 이름Quickstart App을 입력합니다.
    3. 아바타 URLhttps://developers.google.com/chat/images/quickstart-app-avatar.png을 입력합니다.
    4. 설명Quickstart app을 입력합니다.
    5. 기능에서 스페이스 및 그룹 대화 참여를 선택합니다.
    6. 연결 설정에서 HTTP 엔드포인트 URL을 선택합니다.
    7. 트리거에서 모든 트리거에 공통 HTTP 엔드포인트 URL 사용을 선택하고 Cloud Run 함수 트리거의 URL을 상자에 붙여넣습니다.
    8. **공개 상태** 에서 내 도메인의 **특정 사용자 및 그룹에게 이 Chat 앱을 사용할 수 있도록 설정** 을 선택하고 이메일 주소를 입력합니다.
    9. 로그에서 Logging에 오류 로깅을 선택합니다.
  6. 저장 을 클릭합니다.

Chat 앱은 Chat에서 메시지를 수신하고 응답할 준비가 되었습니다.

Chat 앱 테스트

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

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

    Google Chat으로 이동

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

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

Chat 앱의 응답에는 다음 이미지와 같이 보낸사람의 이름과 아바타 이미지를 표시하는 카드 메시지가 포함되어 있습니다.

발신자의 표시 이름과 아바타 이미지가 포함된 카드로 응답하는 Chat 앱

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

문제 해결

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

Chat UI에 오류 메시지가 표시되지 않더라도 Chat 앱의 오류 로깅이 사용 설정되어 있으면 오류를 해결하는 데 도움이 되는 설명 오류 메시지와 로그 데이터를 사용할 수 있습니다. 오류를 보고 디버깅하고 해결하는 데 도움이 필요하면 Google Chat 오류 문제 해결 및 수정을 참고하세요.

정리

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

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

    Resource Manager로 이동

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