사용자가 Google Chat에서 링크를 공유할 때 컨텍스트가 전환되지 않도록 하기 위해 Chat 앱은 메시지에 카드를 첨부하여 링크를 미리 볼 수 있습니다. 이 카드는 사용자가 Google Chat에서 바로 조치를 취할 수 있도록 자세한 정보를 제공합니다.
예를 들어 회사의 모든 고객 서비스 상담사와 Case-y라는 채팅 앱이 포함된 Google Chat 스페이스가 있다고 가정해 보겠습니다. 상담사는 Chat 스페이스에서 고객 서비스 케이스 링크를 자주 공유하며, 동료는 매번 케이스 링크를 열어 담당자, 상태, 제목과 같은 세부정보를 확인해야 합니다. 마찬가지로, 누군가 케이스의 소유권을 가져오거나 상태를 변경하려면 링크를 열어야 합니다.
링크 미리보기를 사용하면 스페이스의 상주 채팅 앱인 Case-y가 케이스 링크를 공유할 때마다 담당자, 상태, 제목을 보여주는 카드를 첨부할 수 있습니다. 상담사는 카드에 있는 버튼을 사용하여 채팅 스트림에서 직접 케이스 소유권을 확인하고 상태를 변경할 수 있습니다.
링크 미리보기 작동 방식
누군가 메시지에 링크를 추가하면 채팅 앱에서 링크를 미리 볼 수 있음을 알려주는 칩이 표시됩니다.

메시지를 보내면 링크가 채팅 앱으로 전송되고 채팅 앱에서 카드를 생성하여 사용자의 메시지에 첨부합니다.

링크 옆에 있는 카드는 버튼과 같은 상호작용 요소를 포함하여 링크에 관한 추가 정보를 제공합니다. 채팅 앱은 버튼 클릭과 같은 사용자 상호작용에 응답하여 첨부된 카드를 업데이트할 수 있습니다.
메시지에 카드를 첨부하여 채팅 앱이 링크를 미리 보지 못하게 하려면 미리보기 칩의 cancel를 클릭하여 미리보기를 차단할 수 있습니다. 사용자는 미리보기 삭제를 클릭하여 언제든지 첨부된 카드를 삭제할 수 있습니다.
채팅 앱에서 미리 볼 수 있도록 Google Cloud 콘솔의 채팅 앱 구성 페이지에 특정 링크(예: example.com
, support.example.com
, support.example.com/cases/
)를 URL 패턴으로 등록합니다.

- Google Cloud 콘솔을 엽니다.
- 'Google Cloud' 옆의 아래쪽 화살표 arrow_drop_down를 클릭하고 채팅 앱의 프로젝트를 엽니다.
- 검색창에
Google Chat API
를 입력하고 Google Chat API를 클릭합니다.
- Manage > Configuration을 클릭합니다.
- 링크 미리보기에서 URL 패턴을 추가하거나 수정합니다.
- 새 URL 패턴에 대한 링크 미리보기를 구성하려면 URL 패턴 추가를 클릭합니다.
- 기존 URL 패턴의 구성을 수정하려면 아래쪽 화살표 expand_more를 클릭합니다.
호스트 패턴 입력란에 URL 패턴의 도메인을 입력합니다. 채팅 앱이 이 도메인으로 연결되는 링크를 미리 봅니다.
subdomain.example.com
과 같은 특정 하위 도메인의 채팅 앱 미리보기 링크를 사용하려면 하위 도메인을 포함하세요.
전체 도메인에 Chat 앱 미리보기 링크를 표시하려면 하위 도메인으로 별표 (*)를 사용하여 와일드 카드 문자를 지정합니다. 예를 들어 *.example.com
는 subdomain.example.com
및 any.number.of.subdomains.example.com
과 일치합니다.
경로 접두어 필드에 호스트 패턴 도메인에 추가할 경로를 입력합니다.
호스트 패턴 도메인의 모든 URL과 일치시키려면 경로 접두어를 비워둡니다.
예를 들어 호스트 패턴이 support.example.com
인 경우 support.example.com/cases/
에서 호스팅되는 케이스의 URL과 일치하도록 하려면 cases/
를 입력합니다.
완료를 클릭합니다.
저장을 클릭합니다.
이제 다른 사용자가 채팅 앱이 포함된 Chat 스페이스의 메시지와 링크 미리보기 URL 패턴이 일치하는 링크를 포함할 때마다 앱에서 링크를 미리 봅니다.
링크 미리보기
지정된 링크의 링크 미리보기를 구성하면 채팅 앱이 추가 정보를 첨부하여 링크를 인식하고 미리 볼 수 있습니다.
채팅 앱이 포함된 Chat 스페이스 내에서 다른 사용자의 메시지에 링크 미리보기 URL 패턴과 일치하는 링크가 포함되어 있으면 채팅 앱이 MESSAGE
상호작용 이벤트를 수신합니다. 상호작용 이벤트의 JSON 페이로드에는 matchedUrl
필드가 포함됩니다.
JSON
message {
. . . // other message attributes redacted
"matchedUrl": {
"url": "https://support.example.com/cases/case123"
},
. . . // other message attributes redacted
}
MESSAGE
이벤트 페이로드에 matchedUrl
필드가 있는지 확인하여 채팅 앱은 미리보기 링크와 함께 메시지에 정보를 추가할 수 있습니다. 채팅 앱은 간단한 문자 메시지로 답장하거나 카드를 첨부할 수 있습니다.
문자 메시지로 답장하기
간단한 응답의 경우 채팅 앱이 링크에 간단한 문자 메시지로 회신하여 링크를 미리 볼 수 있습니다. 이 예시에서는 링크 미리보기 URL 패턴과 일치하는 링크 URL을 반복하는 메시지를 첨부합니다.
카드 첨부
미리보기된 링크에 카드를 연결하려면 UPDATE_USER_MESSAGE_CARDS
유형의 ActionResponse
를 반환합니다. 이 예에서는 간단한 카드를 연결합니다.

Node.js
/**
* Responds to messages that have links whose URLs match URL patterns
* configured for link previewing.
*
* @param {Object} req Request sent from Google Chat.
* @param {Object} res Response to send back.
*/
exports.onMessage = (req, res) => {
if (req.method === 'GET' || !req.body.message) {
res.send(
'Hello! This function is meant to be used in a Google Chat Space.');
}
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (req.body.message.matchedUrl) {
res.json({
'actionResponse': {'type': 'UPDATE_USER_MESSAGE_CARDS'},
'cardsV2': [
{
'cardId': 'attachCard',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [
{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': 'Charlie'}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won"t turn on...',
}
},
],
},
{
'widgets': [
{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {
'action': {
'actionMethodName': 'assign',
},
},
},
},
],
},
],
},
],
},
},
],
});
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
res.json({'text': 'No matchedUrl detected.'});
};
Apps Script
/**
* Responds to messages that have links whose URLs match URL patterns
* configured for link previewing.
*
* @param {Object} event The event object from Chat API.
* @return {Object} Response from the Chat app attached to the message with
* the previewed link.
*/
function onMessage(event) {
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (event.message.matchedUrl) {
return {
'actionResponse': {
'type': 'UPDATE_USER_MESSAGE_CARDS',
},
'cardsV2': [{
'cardId': 'attachCard',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': 'Charlie'}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won\'t turn on...',
},
},
],
},
{
'widgets': [{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {'action': {'actionMethodName': 'assign'}},
},
},
],
}],
}],
},
}],
};
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
return {'text': 'No matchedUrl detected.'};
}
카드 업데이트
미리보기된 링크에 연결된 카드를 업데이트하려면 UPDATE_USER_MESSAGE_CARDS
유형의 ActionResponse
를 반환합니다. 채팅 앱은 채팅 앱 상호작용 이벤트에 대한 응답으로 링크를 미리 보는 카드만 업데이트할 수 있습니다.
채팅 앱은 Chat API를 비동기식으로 호출하여 이러한 카드를 업데이트할 수 없습니다.
링크 미리보기에서는 UPDATE_MESSAGE
유형의 ActionResponse
반환이 지원되지 않습니다. UPDATE_MESSAGE
는 카드만 업데이트하는 것이 아니라 전체 메시지를 업데이트하므로 채팅 앱에서 원본 메시지를 만든 경우에만 작동합니다. 링크 미리보기는 사용자가 만든 메시지에 카드를 연결하므로 채팅 앱에는 메시지를 업데이트할 수 있는 권한이 없습니다.
기능이 사용자가 만든 카드와 앱에서 만든 카드를 모두 업데이트하도록 하려면 채팅 앱 또는 사용자가 메시지를 작성했는지에 따라 ActionResponse
동적으로 설정하세요.
이 작업은 연결된 카드의 onclick
속성 일부로 맞춤 actionMethodName
를 지정 및 확인하거나 (사용자가 만든 메시지임을 식별함) 메시지를 사용자가 작성했는지 확인하는 두 가지 방법이 있습니다.
actionMethodName
를 사용하여 미리보기된 카드에서 CARD_CLICKED
상호작용 이벤트를 올바르게 처리하려면 연결된 카드의 onclick
속성의 일부로 맞춤 actionMethodName
를 설정합니다.
JSON
. . . // Preview card details
{
"textButton": {
"text": "ASSIGN TO ME",
"onClick": {
// actionMethodName identifies the button to help determine the
// appropriate ActionResponse.
"action": {
"actionMethodName": "assign",
}
}
}
}
. . . // Preview card details
"actionMethodName": "assign"
가 버튼을 링크 미리보기의 일부로 식별하면 일치하는 actionMethodName
를 확인하여 올바른 ActionResponse
를 동적으로 반환할 수 있습니다.
옵션 2: 발신자 유형 확인하기
message.sender.type
이 HUMAN
또는 BOT
인지 확인합니다. HUMAN
인 경우 ActionResponse
를 UPDATE_USER_MESSAGE_CARDS
로 설정하고 그렇지 않으면 ActionResponse
를 UPDATE_MESSAGE
로 설정합니다. 방법은 다음과 같습니다.
카드를 업데이트하는 일반적인 이유는 버튼 클릭에 대한 반응입니다. 이전 섹션인 카드 첨부의 나에게 할당 버튼을 상기하세요. 다음은 사용자가 나에게 할당을 클릭한 후 카드가 '나'에게 할당되도록 카드를 업데이트하는 전체 예시입니다. 이 예시에서는 발신자 유형을 확인하여 ActionResponse
를 동적으로 설정합니다.
전체 예: Case-y, 고객 서비스 채팅 앱
다음은 고객 서비스 상담사가 공동작업하는 Chat 스페이스에서 공유된 케이스의 링크를 미리 보는 채팅 앱인 Case-y의 전체 코드입니다.
Node.js
/**
* Responds to messages that have links whose URLs match URL patterns
* configured for link previewing.
*
* @param {Object} req Request sent from Google Chat.
* @param {Object} res Response to send back.
*/
exports.onMessage = (req, res) => {
if (req.method === 'GET' || !req.body.message) {
res.send(
'Hello! This function is meant to be used in a Google Chat Space.');
}
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (req.body.message.matchedUrl) {
res.json(createMessage());
}
// Respond to button clicks on attached cards
if (req.body.type === 'CARD_CLICKED') {
// Checks whether the message event originated from a human or a Chat app
// and sets actionResponse.type to "UPDATE_USER_MESSAGE_CARDS if human or
// "UPDATE_MESSAGE" if Chat app.
const actionResponseType = req.body.action.actionMethodName === 'HUMAN' ?
'UPDATE_USER_MESSAGE_CARDS' :
'UPDATE_MESSAGE';
if (req.body.action.actionMethodName === 'assign') {
res.json(createMessage(actionResponseType, 'You'));
}
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
res.json({'text': 'No matchedUrl detected.'});
};
/**
* Message to create a card with the correct response type and assignee.
*
* @param {string} actionResponseType
* @param {string} assignee
* @return {Object} a card with URL preview
*/
function createMessage(
actionResponseType = 'UPDATE_USER_MESSAGE_CARDS',
assignee = 'Charlie'
) {
return {
'actionResponse': {'type': actionResponseType},
'cardsV2': [
{
'cardId': 'previewLink',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [
{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': assignee}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won"t turn on...',
},
},
],
},
{
'widgets': [
{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {
'action': {
'actionMethodName': 'assign',
},
},
},
},
],
},
],
},
],
}
},
],
};
}
Apps Script
/**
* Responds to messages that have links whose URLs match URL patterns
* configured for link previews.
*
* @param {Object} event The event object from Chat API.
* @return {Object} Response from the Chat app attached to the message with
* the previewed link.
*/
function onMessage(event) {
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (event.message.matchedUrl) {
return {
'actionResponse': {
'type': 'UPDATE_USER_MESSAGE_CARDS',
},
'cardsV2': [{
'cardId': 'previewLink',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': 'Charlie'}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won\'t turn on...',
}
},
],
},
{
'widgets': [{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {'action': {'actionMethodName': 'assign'}}
},
},
],
}],
}],
},
}],
};
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
return {'text': 'No matchedUrl detected.'};
}
/**
* Updates a card that was attached to a message with a previewed link.
*
* @param {Object} event The event object from Chat API.
* @return {Object} Response from the Chat app. Either a new card attached to
* the message with the previewed link, or an update to an existing card.
*/
function onCardClick(event) {
// Checks whether the message event originated from a human or a Chat app
// and sets actionResponse to "UPDATE_USER_MESSAGE_CARDS if human or
// "UPDATE_MESSAGE" if Chat app.
const actionResponseType = event.message.sender.type === 'HUMAN' ?
'UPDATE_USER_MESSAGE_CARDS' :
'UPDATE_MESSAGE';
// To respond to the correct button, checks the button's actionMethodName.
if (event.action.actionMethodName === 'assign') {
return assignCase(actionResponseType);
}
}
/**
* Updates a card to say that "You" are the assignee after clicking the Assign
* to Me button.
*
* @param {String} actionResponseType Which actionResponse the Chat app should
* use to update the attached card based on who created the message.
* @return {Object} Response from the Chat app. Updates the card attached to
* the message with the previewed link.
*/
function assignCase(actionResponseType) {
return {
'actionResponse': {
// Dynamically returns the correct actionResponse type.
'type': actionResponseType,
},
'cardsV2': [{
'cardId': 'assignCase',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': 'You'}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won\'t turn on...',
}
},
],
},
{
'widgets': [{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {'action': {'actionMethodName': 'assign'}},
},
},
],
}],
}],
},
}],
};
}
제한 및 고려사항
채팅 앱의 링크 미리보기를 구성할 때 다음 제한사항 및 고려사항에 유의하세요.
- 각 Chat 앱은 최대 5개의 URL 패턴에 대한 링크 미리보기를 지원합니다.
- Chat 앱은 메시지당 하나의 링크를 미리 볼 수 있습니다. 단일 메시지에 미리보기 가능한 링크가 여러 개 있는 경우 미리보기 가능한 첫 번째 링크만 미리 볼 수 있습니다.
- 채팅 앱은
https://
로 시작하는 링크만 미리 볼 수 있으므로 https://support.example.com/cases/
는 미리 볼 수 있지만 support.example.com/cases/
는 미리 볼 수 없습니다.
- 메시지에 슬래시 명령어와 같이 채팅 앱으로 전송되는 다른 정보가 포함되어 있지 않다면 링크 URL만 링크 미리보기를 통해 채팅 앱으로 전송됩니다.
- 미리보기된 링크에 연결된 카드는 Chat 앱 상호작용 이벤트에 대한 응답으로, 유형
UPDATE_USER_MESSAGE_CARDS
의 ActionResponse
만 지원합니다. 링크 미리보기에서는 UPDATE_MESSAGE
또는 Chat API를 통해 미리보기된 링크에 연결된 카드를 업데이트하는 비동기식 요청을 지원하지 않습니다. 자세한 내용은 카드 업데이트를 참고하세요.
디버그 링크 미리보기
링크 미리보기를 구현할 때 앱의 로그를 읽어 채팅 앱을 디버그해야 할 수도 있습니다. 로그를 읽으려면 Google Cloud 콘솔에서 로그 탐색기로 이동하세요.