Codelab: Gemini를 사용하여 JavaScript로 Chrome 확장 프로그램 빌드하기

1. 소개

Meet 통화에 참여하지만 다른 사람이 먼저 참여하고 싶지 않으신가요? 이 문제에 해당한다면 해결책이 있습니다.

이 Codelab에서는 첫 번째 참여자가 통화에 참여하면 알림을 표시하는 Chrome 확장 프로그램을 만들어 보겠습니다.

Chrome 확장 프로그램의 다양한 요소를 학습한 후 확장 프로그램의 각 부분에 대해 자세히 살펴봅니다. 콘텐츠 스크립트, 서비스 워커 및 메시지 전달과 같은 확장 기능에 대해 학습합니다.

참여자가 Meet 통화에 참여할 때마다 알림을 받으려면 매니페스트 v3 버전을 준수해야 합니다.

2. 시작하기 전에

기본 요건

이 Codelab은 초보자에게 적합하지만 JavaScript에 관한 기본적인 이해가 있으면 경험을 크게 향상할 수 있습니다.

설정/요구사항

  • Chrome 브라우저
  • 로컬 시스템의 IDE/편집기 설정
  • gcloud를 사용하여 Gemini API를 사용 설정하려면 gcloud cli를 설치하세요.

Gemini API 사용 설정

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"에 확장 프로그램이 표시됩니다.

3d802a497ce0cfc2.png

92db1999a1800ecd.png

좋습니다. 확장 프로그램이 표시되지만 몇 가지 기능을 추가해 보겠습니다.

4. 콘텐츠 스크립트 추가

콘텐츠 스크립트를 사용하여 실행할 수 있는 https://meet.google.com에서만 일부 자바스크립트 코드를 실행하려고 합니다. 확장 프로그램에서 이 목표를 달성하는 방법을 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 파일에 필요한 자바스크립트를 제공합니다.

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"]
        }
    ]
}

이렇게 하면 하위 도메인의 페이지로 이동할 때마다 content.js 콘텐츠 스크립트를 삽입하도록 Chrome에 지시합니다. https://meet.google.com에서 확인할 수 있습니다. 이 파일을 추가하고 테스트해 보겠습니다.

이 코드를 content.js 파일에 추가해 보겠습니다.

console.log("Hello Gemini");

당연하지! meet.google.com에 방문하면 'Hello Gemini'가 표시됩니다. 를 클릭합니다(Mac: Cmd + Opt + J / Win/Linux: Ctrl + Shift + J).

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("안녕하세요. Gemini!");

6216bab627c31e6c.png

d61631cd9962ffe5.png

좋습니다. 이제 앱에 JavaScript 관련 기능을 추가할 수 있습니다. 잠시 시간을 내어 우리가 달성하고자 하는 목표에 대해 생각해 보겠습니다.

콘텐츠 스크립트 개선

다른 사용자가 회의에 참여하면 회의 페이지(회의 참여 옵션이 있는 경우)에서 알림을 받을 수 있도록 하고 싶습니다. 이를 위해 회의가 비어 있을 때와 다른 사람이 회의에 참여했을 때 화면이 어떻게 시각적으로 바뀌는지 관찰해 보겠습니다.

회의에 아무도 없을 때 표시되는 화면은 다음과 같습니다.

fe5a0c95b20e7f72.png

반면 회의에 일부 참석자가 있을 때의 시각 자료입니다.

7a5ef60521d961cc.png

테스트 직후에 두 가지 눈에 띄는 차이점을 확인할 수 있습니다.

  1. 상태 텍스트가 '다른 사용자 없음'에서 변경됩니다. '[사용자] 님이 통화에 참여 중입니다.'로 변경합니다.
  2. 통화에 참여한 사용자의 이미지가 표시됩니다.

이 두 가지 변경사항으로 회의에 참여한 사람이 있는지 확인할 수 있지만, 후자를 사용하면 이미 참여한 사용자의 정보를 얻을 수 있으므로 이 방법을 사용해 보겠습니다.

'Elements Inspector'를 엽니다. Chrome에서 핫키 ( Mac: Cmd + Opt + C / Win: Ctrl + Shift + C)를 사용하여 로그인한 다음 참여한 사용자의 이미지를 클릭합니다.

이미지에 수업 문자열이 거의 없는데 이미지의 제목 속성에는 회의에 참여한 사용자의 이름이 포함되어 있습니다. 또한 이 이미지 태그는 U04fid 클래스를 사용하여 div에 래핑됩니다. 테스트 회의에 참석자를 추가하면 이 상위 div가 여러 이미지(서로 다른 사용자에 해당)를 호스팅하는 것을 확인할 수 있습니다.

따라서 다음과 같은 부분적인 전략을 염두에 두겠습니다.

  1. U04fid 클래스를 포함하는 div에 요소가 포함되어 있으면 감지합니다.
  2. 표시된다면 회의에 참석자가 있음을 의미합니다.
  3. 사용자에게 알립니다.

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로 바꿉니다.

b2c22f064a3f2d9c.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.sendMessagechrome.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 또는 문서를 사용하여 잘못된 부분을 파악하고 문제를 해결하기 위한 다양한 방법을 수집하세요.

필요한 도움을 받으려면 프롬프트를 수정해야 하는 경우가 많지만, 모든 상황별 여정이 저장된 하나의 탭에서 이를 수행할 수 있습니다.