1. 소개
Meet 통화에 참여하려고 하는데 첫 번째로 참여하고 싶지 않으신가요? 이러한 경우를 위한 솔루션이 있습니다.
이 Codelab에 따라 첫 번째 참여자가 통화에 참여할 때 알림을 보내는 Chrome 확장 프로그램을 만듭니다.
Chrome 확장 프로그램의 다양한 요소를 알아본 다음 확장 프로그램의 각 부분을 자세히 살펴봅니다. 콘텐츠 스크립트, 서비스 워커, 메시지 전달과 같은 확장 프로그램 함수에 대해 알아봅니다.
참여자가 Meet 통화에 참여할 때마다 알림을 받으려면 매니페스트 v3 출시를 준수해야 합니다.
2. 시작하기 전에
기본 요건
이 Codelab은 초보자에게 적합하지만 JavaScript에 대한 기본적인 이해가 있으면 환경을 크게 개선할 수 있습니다.
설정/요구사항
- Chrome 브라우저
- 로컬 시스템에 IDE/편집기 설정
- gcloud를 사용하여 Gemini API를 사용 설정하려면 gcloud CLI를 설치합니다.
Gemini API 사용 설정
- Google Cloud 콘솔의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.
- Cloud 프로젝트에 결제가 사용 설정되어 있어야 하므로 프로젝트에 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요.
- API를 사용 설정하려면 Gemini Marketplace 페이지로 이동하세요. gcloud를 사용하여 API를 사용 설정할 수도 있습니다.
gcloud services enable cloudaicompanion.googleapis.com --project PROJECT_ID - 새 탭에서 Gemini for Cloud 콘솔 페이지를 방문하고 '채팅 시작'을 클릭합니다.
Note that if you're writing the code in the Cloud Shell editor, then you will have to download the folder somewhere on your local filesystem to test the extension locally.
3. 산타 추적 시작하기
기본 확장 프로그램 설치
프로젝트의 루트로 사용할 디렉터리를 만들어 보겠습니다.
mkdir gemini-chrome-ext
cd gemini-chrome-ext
Gemini에 구체적인 질문을 하기 전에 Chrome 확장 프로그램의 일반적인 구조에 관해 몇 가지 질문을 해 보겠습니다.
프롬프트:
What are the important parts to build a chrome extension?
manifest 파일, background script, 사용자 인터페이스에 관한 세부정보를 지정하는 응답이 표시됩니다. 이러한 특정 파일에 관해 자세히 알아보겠습니다.
프롬프트:
Create a manifest.json file to build a chrome extension. Make the name of the extension "Meet Joinees Notifier" and the author "<YOUR_EMAIL>"
작성자 필드에 원하는 이름과 이메일을 사용할 수 있습니다.
Gemini는 필요한 매니페스트 파일의 콘텐츠를 반환하지만 action 필드와 같이 필요하지 않은 추가 필드도 가져옵니다. 또한 설명이 필요합니다. 이 문제를 해결해 보겠습니다.
프롬프트:
Remove the "action" field and make the description as "Adds the ability to receive a notification when a participant joins a Google meet".
이 콘텐츠를 프로젝트의 루트에 있는 manifest.json 파일에 넣어 보겠습니다.
이 단계에서 매니페스트 파일은 다음과 같이 표시됩니다.
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "<YOUR_EMAIL>"
}
이 Codelab에서는 매니페스트 파일에 이러한 필드가 있다고 가정하므로 지금은 매니페스트 파일에서 생성된 다른 추가 필드를 삭제합니다.
이제 확장 프로그램이 작동하는지 어떻게 테스트할까요? Gemini에게 물어보겠습니다.
프롬프트:
Guide me on the steps needed to test a chrome extension on my local filesystem.
테스트 방법에 관한 몇 가지 단계가 제공됩니다. chrome://extensions로 이동하여 "Extensions Page"로 이동하고 "Developer Mode" 버튼을 사용 설정해야 합니다. 그러면 "Load unpacked" 버튼이 표시됩니다. 이 버튼을 사용하여 확장 프로그램 파일이 로컬로 포함된 폴더로 이동할 수 있습니다. 이렇게 하면 "Extensions Page"에서 확장 프로그램을 볼 수 있습니다.


좋습니다. 확장 프로그램이 표시되지만 몇 가지 기능을 추가해 보겠습니다.
4. 콘텐츠 스크립트 추가
콘텐츠 스크립트를 사용하여 https://meet.google.com에서만 JavaScript 코드를 실행하려고 합니다. 확장 프로그램에서 이를 달성하는 방법을 Gemini에게 물어보겠습니다.
프롬프트:
How to add a content script in our chrome extension?
또는 더 구체적으로:
프롬프트:
How to add a content script to run on meet.google.com subdomain in our chrome extension?
또는 다른 버전:
프롬프트:
Help me add a content script named content.js to run on meet.google.com subdomain in our chrome extension. The content script should simply log "Hello Gemini" when we navigate to "meet.google.com".
Gemini는 manifest.json 파일에서 변경해야 하는 정확한 변경사항과 content.js 파일에 필요한 JavaScript를 제공합니다.
content_scripts를 추가하면 매니페스트 파일이 다음과 같이 됩니다.
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "abc@example.com",
"content_scripts": [
{
"matches": ["https://meet.google.com/*"],
"js": ["content.js"]
}
]
}
이렇게 하면 하위 도메인 "https://meet.google.com"의 페이지로 이동할 때마다 Chrome에서 콘텐츠 스크립트 content.js를 삽입합니다. 이 파일을 추가하고 테스트해 보겠습니다.
content.js 파일에 이 코드를 추가해 보겠습니다.
console.log("Hello Gemini");
물론입니다. meet.google.com을 방문하면 JavaScript 콘솔(Mac: Cmd + Opt + J / Windows/Linux: Ctrl + Shift + J)에 "Hello Gemini"가 표시됩니다.
manifest.json
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "luke@cloudadvocacyorg.joonix.net",
"permissions": [
"tabs",
"notifications"
],
"content_scripts": [
{
"matches": [
"https://meet.google.com/*"
],
"js": [
"content.js"
]
}
]
}
content.js
console.log("Hello Gemini!");


좋습니다. 이제 앱에 JavaScript 관련 기능을 추가할 수 있습니다. 잠시 시간을 내어 달성하려는 목표를 생각해 보겠습니다.
콘텐츠 스크립트 개선
회의 페이지(회의에 참여하는 옵션이 있는 페이지)에 있을 때 누군가가 회의에 참여하면 알림을 받을 수 있도록 하고 싶습니다. 이를 달성하려면 회의가 비어 있을 때와 누군가가 회의에 참여했을 때 화면이 시각적으로 어떻게 변경되는지 관찰해 보겠습니다.
회의에 아무도 없을 때의 모습은 다음과 같습니다.

회의에 참여자가 있을 때의 시각적 요소는 다음과 같습니다.

바로 눈에 띄는 두 가지 차이점이 있습니다.
- 상태 텍스트가 '아무도 없습니다'에서 '[사용자] 님이 이 통화에 참여했습니다'로 변경됩니다.
- 통화에 참여한 사용자의 이미지를 볼 수 있습니다.
회의에 참여한 사람이 있는지 확인하려면 이러한 변경사항이 모두 유용하지만 후자의 경우 이미 참여한 사용자에 관한 정보를 얻을 수 있으므로 이를 사용해 보겠습니다.
단축키 ( Mac: Cmd + Opt + C / Win: Ctrl + Shift + C)를 사용하여 Chrome에서 "요소 검사기"를 연 다음 참여한 사용자의 이미지를 클릭합니다.
클래스 문자열이 몇 개 있는 이미지가 있고 이미지의 제목 속성에 회의에 참여한 사용자의 이름이 있습니다. 또한 이 이미지 태그는 클래스 U04fid가 있는 div로 래핑됩니다. 테스트 회의에 참여자를 추가하면 이 상위 div에 여러 이미지(서로 다른 사용자에 해당)가 호스팅되는 것을 볼 수 있습니다.
따라서 다음과 같은 부분적인 전략이 있습니다.
- 클래스
U04fid가 있는 div에 요소가 포함되어 있는지 감지합니다. - 포함되어 있다면 회의에 참여자가 있다는 의미입니다.
- 사용자에게 알립니다.
Gemini로 돌아가서 이 단계를 단계별로 달성하는 방법을 물어보겠습니다.
프롬프트:
How to detect for element changes in the HTML page?
MutationObserver와 관련된 솔루션을 제공하지만 요구사항과 관련된 솔루션은 제공하지 않습니다. 프롬프트를 수정해 보겠습니다.
프롬프트:
How can I detect if an element "div.U04fid" has any child images in the HTML page?
이번에는 MutationObserver가 포함되지 않지만 상위 div를 사용하여 참여자를 확인하는 솔루션을 제공합니다.
수신한 내용은 다음과 같습니다.
const divU04fid = document.querySelector('div.U04fid');
const childImages = divU04fid.querySelectorAll('img');
if (childImages.length > 0) {
// div.U04fid has at least one child image.
}
접근 방식을 더 수정해 보겠습니다.
프롬프트:
Create a method which checks if the element div.U04fid has any child elements, if it does it should log it on the console. The method should be called checkJoinees and it should be called as a callback for MutationObserver which is observing the document.body.
다음 코드 스니펫이 반환됩니다.
function checkJoinees(mutations) {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// A child node was added or removed.
if (document.querySelector('div.U04fid') && document.querySelector('div.U04fid').childElementCount > 0) {
// div.U04fid has at least one child element.
console.log('div.U04fid has at least one child element.');
}
}
}
return false;
}
const observer = new MutationObserver(checkJoinees);
observer.observe(document.body, {
childList: true,
delay: 1000
});
이 코드를 content.js 파일에 복사하고 확장 프로그램을 다시 로드한 다음 Meet 페이지를 다시 로드합니다.
이제 누군가가 회의에 참여하면 콘솔에서 로그 문을 볼 수 있습니다.
5. 사용자에게 알림 보내기
이제 참여자가 회의에 참여한 시점을 감지할 수 있으므로 Chrome 확장 프로그램에 알림 부분을 추가해 보겠습니다. Chrome 확장 프로그램의 문서를 찾아보거나 프롬프트를 조정하여 원하는 것을 알 수 있지만 기본적으로 chrome.notifications.create API를 사용해야 하며 이 메서드 호출은 백그라운드 서비스 워커에서 이루어져야 합니다.
프롬프트:
Using the documentation for chrome notifications tell me how to use the chrome.notifications.create method.
주요 내용은 다음과 같은 자세한 단계가 표시됩니다.
- 매니페스트 파일에
notifications권한을 추가합니다. - chrome.notifications.create를 호출합니다.
- 호출은 백그라운드 스크립트에 있어야 합니다.
manifest version 3에서 Chrome 확장 프로그램에 백그라운드 스크립트를 추가하려면 manifest.json 파일에 background.service_worker 선언이 필요합니다.
따라서 background.js라는 파일을 만들고 manifest.json 파일에 다음을 추가합니다.
"background": {
"service_worker": "background.js"
},
"permissions": [
"notifications"
]
위의 추가사항을 적용하면 매니페스트 파일이 다음과 같이 됩니다.
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "<YOUR_EMAIL>",
"content_scripts": [
{
"matches": ["https://meet.google.com/*"],
"js": ["content.js"]
}
],
"background": {
"service_worker": "background.js"
},
"permissions": [
"notifications"
]
}
프롬프트:
Create a method sendNotification that calls the chrome.notifications.create method with the message, "A user joined the call" for a chrome extension with manifest v3, the code is in the background service worker
이 이미지를 폴더의 루트에 저장하고 이름을 success.png로 바꿉니다.

그런 다음 다음 코드 스니펫을 background.js에 추가합니다.
function sendNotification(notificationId, message) {
chrome.notifications.create(notificationId, {
type: "basic",
title: "A user joined the call",
message: message,
iconUrl: "./success.png"
});
}
sendNotification("notif-id", "test message");
이제 확장 프로그램 페이지에서 확장 프로그램을 다시 로드하면 알림 팝업이 즉시 표시됩니다.
6. Chrome 확장 프로그램에 메시지 전달 추가
이제 필요한 마지막 주요 단계는 참여자의 콘텐츠 스크립트 감지와 백그라운드 스크립트의 sendNotification 메서드를 연결하는 것입니다. Chrome 확장 프로그램의 컨텍스트에서 이를 수행하는 방법은 message passing이라는 기술을 사용하는 것입니다.
이렇게 하면 Chrome 확장 프로그램의 여러 부분 간에 통신이 가능합니다. 이 경우 콘텐츠 스크립트에서 백그라운드 서비스 워커로 통신합니다. Gemini에게 이를 달성하는 방법을 물어보겠습니다.
프롬프트:
How to send a message from the content script to the background script in a chrome extension
Gemini는 chrome.runtime.sendMessage 및 chrome.runtime.onMessage.addListener에 대한 관련 호출로 응답합니다.
기본적으로 sendMessage를 사용하여 누군가가 Meet 통화에 참여했다는 콘텐츠 스크립트의 메시지를 보내고 onMessage.addListener를 콘텐츠 스크립트에서 보낸 메시지에 반응하는 이벤트 리스너로 사용합니다. 이 경우 이 이벤트 리스너에서 sendNotification 메서드 호출을 트리거합니다.
알림 메시지와 action 속성을 백그라운드 서비스 워커에 전달합니다. action 속성은 백그라운드 스크립트가 응답하는 대상을 설명합니다.
다음은 content.js 코드입니다.
function checkJoinees(mutations) {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// A child node was added or removed.
if (document.querySelector('div.U04fid') && document.querySelector('div.U04fid').childElementCount > 0) {
// div.U04fid has at least one child element.
sendMessage();
}
}
}
return false;
}
const observer = new MutationObserver(checkJoinees);
observer.observe(document.body, {
childList: true,
delay: 1000
});
function sendMessage() {
chrome.runtime.sendMessage({
txt: "A user has joined the call!",
action: "people_joined"
});
}
다음은 background.js 코드입니다.
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "people_joined") {
sendNotification("notif-id", message.txt);
}
});
function sendNotification(notificationId, message) {
chrome.notifications.create(notificationId, {
type: "basic",
title: "A user joined the call",
message: message,
iconUrl: "./success.png"
});
}
알림 메시지를 맞춤설정하고 고유한 알림 ID를 가져와 보겠습니다. 알림 메시지에 사용자의 이름을 포함할 수 있습니다. 이전 단계에서 이미지의 제목 속성에 사용자의 이름이 표시되는 것을 기억할 수 있습니다. 따라서 document.querySelector('div.U04fid > img').getAttribute('title').를 사용하여 참여자의 이름을 가져올 수 있습니다.
알림 ID와 관련하여 콘텐츠 스크립트의 탭 ID를 가져와서 알림 ID로 사용할 수 있습니다. 이는 sender.tab.id.를 사용하여 이벤트 리스너 chrome.runtime.onMessage.addListener 내에서 수행할 수 있습니다.
마지막으로 파일은 다음과 같이 표시됩니다.
manifest.json
{
"name": "Meet Joinees Notifier",
"version": "1.0",
"manifest_version": 3,
"description": "Adds the ability to receive a notification when a participant joins a Google Meet",
"author": "<YOUR_EMAIL>",
"content_scripts": [
{
"matches": ["https://meet.google.com/*"],
"js": ["content.js"]
}
],
"background": {
"service_worker": "background.js"
},
"permissions": [
"notifications"
]
}
content.js
function checkJoinees(mutations) {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
// A child node was added or removed.
if (document.querySelector('div.U04fid') && document.querySelector('div.U04fid').childElementCount > 0) {
const name = document.querySelector('div.U04fid > img').getAttribute('title');
sendMessage(name);
}
}
}
return false;
}
const observer = new MutationObserver(checkJoinees);
observer.observe(document.body, {
childList: true,
delay: 1000
});
function sendMessage(name) {
const joinee = (name === null ? 'Someone' : name),
txt = `${joinee} has joined the call!`;
chrome.runtime.sendMessage({
txt,
action: "people_joined",
});
}
background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "people_joined") {
sendNotification("" + sender.tab.id, message.txt); // We are casting this to string as notificationId is expected to be a string while sender.tab.id is an integer.
}
});
function sendNotification(notificationId, message) {
chrome.notifications.create(notificationId, {
type: "basic",
title: "A user joined the call",
message: message,
iconUrl: "./success.png"
});
}
7. 축하합니다
짧은 시간 안에 Gemini의 도움을 받아 Chrome 확장 프로그램을 빌드할 수 있었습니다. 숙련된 Chrome 확장 프로그램 개발자이든 확장 프로그램의 세계를 처음 접하는 개발자이든 Gemini는 달성하려는 모든 작업을 지원할 수 있습니다.
Chrome 확장 프로그램으로 할 수 있는 다양한 작업에 관해 질문해 보시기 바랍니다. chrome.storage, alarms 등과 같이 찾아볼 만한 API가 많이 있습니다. 막히는 부분이 있으면 Gemini 또는 문서를 사용하여 잘못된 부분을 파악하거나 문제를 해결하는 다양한 방법을 모색하세요.
필요한 도움을 받으려면 프롬프트를 수정해야 하는 경우가 많지만 모든 컨텍스트 여정을 유지하는 하나의 탭에서 이 작업을 수행할 수 있습니다.