크롬 확장 프로그램 만들기

크롬 확장 프로그램 만드는 방법을 검색하면, 대부분 manifest.json 1, 2버전의 포스팅들이라 manifest version 3에 관한 크롬 개발자 공식 튜토리얼을 보면서 따라해본 내용을 포스팅.
이 튜토리얼은 사용자가 현재 포커스가 있는 페이지의 배경색을 변경할 수 있도록 하는 확장을 빌드한다.

구성 요소

extensions(이하 확장)의 구성 요소에는 background scripts, content scripts, options page, UI elements 및 다양한 논리 파일이 포함된다.
확장 구성 요소는 기능에 따라 HTML, CSS, JavaScript와 같은 웹 개발 기술로 생성되며 모든 옵션이 필요한 것은 아니다.

manifest.json 만들기

확장 프로그램의 필수 구성 요소에는 manifest.json 파일이 필요하며, manifest.json에 들어가야할 코드는 다음과 같다.

// manifest.json
{
  "name": "Getting Started Example", // 확장 프로그램의 이름
  "description": "Build an Extension!", // 확장 프로그램에 대한 설명
  "version": "1.0", // 확장 프로그램의 버전
  "manifest_version": 3 // manifest 버전
}

위와 같이 manifest.json을 만들고 나면, 해당 파일이 있는 디렉토리를 개발자 모드에서 확장 프로그램으로 추가 할 수 있다.

image

  • 확장 프로그램 관리페이지(chrome://extensions): 접속
  • 개발자 모드: 켜기
  • 압축해제된 확장 프로그램을 로드: 클릭
  • 해당 디렉토리 선택

image 완료하면 위와 같이 확장 프로그램이 등록되어 실행할 수 있는 것을 볼 수 있다.
(기능은 구현되지 않아 실제 실행되는 건 없다.)

기능 추가

매니페스트에 백그라운드 스크립트 추가

매니페스트에 백그라운드 스크립트를 등록하면 어떤 파일을 참조해야 하는지, 그리고 해당 파일이 어떻게 동작해야 하는지 확장명이 알려준다.

{
  "name": "Getting Started Example",
  "description": "Build an Extension!",
  "version": "1.0",
  "manifest_version": 3,
  "background": { // 백그라운드 스크립트
    "service_worker": "background.js" // manifest version 3 이후에 변경 됨.
  }
}

manifest version 3에서는 백그라운드 스크립트 등록하는 방법이 기존과 달라졌다. 버전 3부터는 service_worker가 포함 되었으며, 확장 프로그램을 새로고침하면 Chrome은 지정된 파일에서 수신 대기해야 하는 중요한 이벤트와 같은 추가 지침을 스캔한다.

백그라운드 스크립트 만들기

위에서 매니페스트에 추가한 background.js를 생성해주자.

// background.js

let color = '#3aa757'; // 배경색 데이터

chrome.runtime.onInstalled.addListener(() => { // runtime.onInstalled에 대한 수신 이벤트를 포함하여 시작.
  chrome.storage.sync.set({ color }); // 배경색 데이터를 storage에 저장.
  console.log('Default background color set to %cgreen', `color: ${color}`); // console.log를 통해 저장된 배경색을 확인.
});

Chrome은 chrome.runtime등과 같은 많은 특수 목적 API가 포함된 기능을 제공한다. 대부분의 chrome.* API의 메서드는 비동기식으로, 작업이 완료될 때까지 기다리지 않고 즉시 반환한다. 작업의 결과를 알아야 하는 경우, 콜백 함수를 메서드에 전달한다.

위에서 사용한 chrome.runtime API는 background page를 검색, 매니페스트에 대한 세부 정보를 반환, 앱 또는 확장 프로그램 lifecycle의 이벤트를 수신 대기하고 응답하는데 사용한다. 또한 이 API를 사용하여 URL의 상대 경로를 정규화된 URL로 변환할 수 도 있다.

runtime.onInstalledchrome.runtime의 이벤트 중 확장 프로그램이 처음 설치될 때나 새 버전으로 업데이트될 때, 또는 Chrome이 새 버전으로 업데이트될 때 실행된다.

chrome.storage API는 사용자가 데이터에 대한 변경 사항을 저장, 검색 및 추적할 때 사용한다.
사용자 데이터의 저장은 storage.sync 또는 storage.local을 사용하여 할 수 있으며, storage.sync 사용자가 동기화를 활성화한 경우 저장된 데이터를 사용자가 로그인한 모든 Chrome 브라우저에 자동으로 동기화한다.
Chrome이 오프라인 상태이면 Chrome은 데이터를 로컬에 데이터를 저장하고, 다음 번에 브라우저가 온라인 상태가 되면 크롬은 데이터를 동기화한다.
사용자가 동기화를 사용하지 않는 경우에도 storage.sync는 계속 작동한다. 이 경우 storage.local과 동일하게 동작합니다.
storage.syncstorage.local의 차이점은 여기에서 확인할 수 있다.
storage에 저장 되는 데이터는 암호화 되지 않기 때문에 기밀 사용자 정보를 저장해서는 안된다.
chrome.storage API 등 대부분의 chrome API는 매니페스트 안의 permissions필드에 등록해야만 확장 프로그램에서 사용할 수 있다.
따라서 위에서 만든 백그라운드 스크립트는 당장 제대로 동작하지 않는다. chrome.storage API 사용권한을 매니페스트에 등록하여 동작시켜보자.

저장 권한(storage permission) 추가

{
  "name": "Getting Started Example",
  "description": "Build an Extension!",
  "version": "1.0",
  "manifest_version": 3,
  "background": {
    "service_worker": "background.js"
  },
  "permissions": ["storage"]
}

저장 권한을 추가하였다. 백그라운드 스크립트가 정상적으로 동작하는지 확인해보자. image

  • 우측 하단의 새로고침: 클릭
  • 새로 나타난 뷰 검사 서비스 워커: 클릭 image 콘솔창에 다음과 같이 #3aa757 색상의 green 문자열이 출력되면 정상적으로 실행된 것이다.

유저 인터페이스 추가

popup.html 생성

확장 프로그램은 다양한 형태의 UI를 가질 수 있는데, 주로 사용하는 popup을 사용하여 만들어보자. popup은 확장 프로그램의 아이콘을 클릭했을 때 나타나는 페이지를 의미하며 popup.html, index.html과 같은 페이지를 만들어서 사용한다. 해당 디렉토리에 popup.html파일을 생성하자.

<!-- popup.html -->
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="button.css">
  </head>
  <body>
    <button id="changeColor"></button>
  </body>
</html>

해당 팝업 매니페스트에 선언

백그라운드 스크립트와 마찬가지로 popup.html이 동작하기 위해선 매니페스트에 선언되어야 한다. 방법은 매니페스트에 action객체를 넣고, 그 안에 default_popuppopup.html로 설정하면 된다.

{
 "name": "Getting Started Example",
 "description": "Build an Extension!",
 "version": "1.0",
 "manifest_version": 3,
 "background": {
   "service_worker": "background.js"
 },
 "permissions": ["storage"],
 "action": {
   "default_popup": "popup.html"
 }
}

popup.html 코드를 살펴보면 button.css라는 외부 CSS파일을 참조한다. 해당 디렉토리에 같은 이름을 가진 파일을 생성하고 코드를 작성하자.

/* button.css */
button {
  height: 30px;
  width: 30px;
  outline: none;
  margin: 10px;
  border: none;
  border-radius: 2px;
}

button.current {
  box-shadow: 0 0 0 2px white,
              0 0 0 4px black;
}

아이콘 이미지 설정

툴바 아이콘에 관한 설정도 manifest의 action에 포함된다. action에서 default_icon 필드를 추가해 작성하자.

{
  "name": "Getting Started Example",
  "description": "Build an Extension!",
  "version": "1.0",
  "manifest_version": 3,
  "background": {
    "service_worker": "background.js"
  },
  "permissions": ["storage"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "/images/get_started16.png",
      "32": "/images/get_started32.png",
      "48": "/images/get_started48.png",
      "128": "/images/get_started128.png"
    }
  }
}

해당 경로에 위치하는 파일은 여기에서 다운로드 할 수 있다. 다운 받아 같은 디렉토리에 위치시켜준다. 코드를 보면 default_icon의 경우 16, 32, 48, 128 별로 다른 파일을 설정하는데 이 숫자는 가로,세로의 px를 의미한다. 임의로 설정한 것은 아니며 기본적으로 구글에서 default로 요구하는 사이즈 같다.

확장 프로그램 관리페이지나 권한경고 및 favicon에도 해당 이미지를 표시할 수 있는데, 방법은 아래와 같다.

{
  "name": "Getting Started Example",
  "description": "Build an Extension!",
  "version": "1.0",
  "manifest_version": 3,
  "background": {
    "service_worker": "background.js"
  },
  "permissions": ["storage"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "/images/get_started16.png",
      "32": "/images/get_started32.png",
      "48": "/images/get_started48.png",
      "128": "/images/get_started128.png"
    }
  },
  "icons": {
    "16": "/images/get_started16.png",
    "32": "/images/get_started32.png",
    "48": "/images/get_started48.png",
    "128": "/images/get_started128.png"
  }
}

코드를 저장한 후 확장 프로그램 관리페이지에 들어가 새로고침을 실행하면 아이콘이 적용된 것을 확인할 수 있다. 그리고 해당 확장 프로그램을 클릭하여 실행하면, 기본 색상의 버튼이 표시되는 팝업이 열린다.

팝업 버튼 색 추가

팝업 UI의 마지막 단계는 버튼에 색을 추가하는 것이다. 디렉토리에 popup.js파일을 생성하고 다음 코드를 부여한다.

// popup.js
// Initialize button with user's preferred color
let changeColor = document.getElementById("changeColor");

chrome.storage.sync.get("color", ({ color }) => {
  changeColor.style.backgroundColor = color;
});"

작성한 스크립트를 popup.html에 추가하여, 적용시킨다.

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="button.css">
  </head>
  <body>
    <button id="changeColor"></button>
    <script src="popup.js"></script>
  </body>
</html>

다시 확장 프로그램을 새로고침하여 실행하면 기존에 설정한 #3aa757 색상으로 버튼이 바뀐 것을 볼 수 있다.

Layer Logic 추가

이제 사용자가 버튼을 클릭했을 때, 실행될 Logic을 구현해보자. popup.js 끝에 다음과 같이 코딩한다.

// When the button is clicked, inject setPageBackgroundColor into current page
changeColor.addEventListener("click", async () => {
  let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

  chrome.scripting.executeScript({
    target: { tabId: tab.id },
    function: setPageBackgroundColor,
  });
});

// The body of this function will be executed as a content script inside the
// current page
function setPageBackgroundColor() {
  chrome.storage.sync.get("color", ({ color }) => {
    document.body.style.backgroundColor = color;
  });
}

추가된 코드의 내용은 버튼에 click 이벤트 리스너를 통해 컨텐트 스크립트를 프로그래밍 방식으로 주입하는 것이다.
위의 let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });처럼 현재 페이지(탭)에 대한 임시 접근 허용을 위해서는 activeTab 권한과 chrome.scripting.executeScript처럼 Scripting API의 excuteScript 메서드를 사용하기 위해선 scripting 권한이 필요하다. 해당 권한을 매니페스트를 통해 추가해준다.

{
  "name": "Getting Started Example",
  ...
  "permissions": ["storage", "activeTab", "scripting"],
  ...
}

이제 확장 프로그램이 정상적으로 작동할 것이다. 모두 저장하고, 리로드하여 테스트를 해보자.

사용자에게 옵션 제공

이 확장 프로그램의 기능은 배경을 녹색으로만 변경할 수 있다. 옵션 페이지를 추가하여 사용자가 확장 기능에 대한 제어권을 더 많이 부여하고 정의할 수 있다. 해당 디렉토리에 options.html을 만들어 다음과 같이 코드를 추가한다.

<!-- options.html -->
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="button.css" />
  </head>
  <body>
    <div id="buttonDiv"></div>
    <div>
      <p>Choose a different background color!</p>
    </div>
  </body>
  <script src="options.js"></script>
</html>

그런 다음 옵션을 동작시키기 위해 매니페스트에 해당 페이지를 추가한다

{
  "name": "Getting Started Example",
  ...
  "options_page": "options.html"
}

확장 프로그램을 리로드하면 아래와 같이 옵션이 활성화 되는 것으 볼 수 있다. image 마지막으로 옵션 로직을 추가하자. 해당 디렉토리에 options.js를 생성하고 다음과 같이 코드를 작성한다.

// options.js
let page = document.getElementById("buttonDiv");
let selectedClassName = "current";
const presetButtonColors = ["#3aa757", "#e8453c", "#f9bb2d", "#4688f1"];

// Reacts to a button click by marking the selected button and saving
// the selection
function handleButtonClick(event) {
  // Remove styling from the previously selected color
  let current = event.target.parentElement.querySelector(
    `.${selectedClassName}`
  );
  if (current && current !== event.target) {
    current.classList.remove(selectedClassName);
  }

  // Mark the button as selected
  let color = event.target.dataset.color;
  event.target.classList.add(selectedClassName);
  chrome.storage.sync.set({ color });
}

// Add a button to the page for each supplied color
function constructOptions(buttonColors) {
  chrome.storage.sync.get("color", (data) => {
    let currentColor = data.color;
    // For each color we were provided…
    for (let buttonColor of buttonColors) {
      // …create a button with that color…
      let button = document.createElement("button");
      button.dataset.color = buttonColor;
      button.style.backgroundColor = buttonColor;

      // …mark the currently selected color…
      if (buttonColor === currentColor) {
        button.classList.add(selectedClassName);
      }

      // …and register a listener for when that button is clicked
      button.addEventListener("click", handleButtonClick);
      page.appendChild(button);
    }
  });
}

// Initialize the page by constructing the color options
constructOptions(presetButtonColors);

function constructOptions(buttonColors)을 통해 4가지 색상 옵션이 온클릭 이벤트 수신기가 있는 옵션 페이지에서 버튼으로 생성된다. 사용자가 버튼을 클릭하면 확장 프로그램의 저장소(storage)에 있는 color 값이 업데이트된다.
확장 프로그램의 모든 파일이 이 저장소에서 색상 정보를 가져오므로 다른 값을 업데이트할 필요가 없다.


참고사이트
https://developer.chrome.com/docs/extensions/mv3/getstarted/ https://developer.chrome.com/docs/extensions/reference/ https://developer.chrome.com/docs/extensions/mv3/manifest/activeTab/ https://developer.chrome.com/docs/extensions/mv3/content_scripts/#programmatic

📝 사실은 내가 보려고 기록한 것 😊
피드백 댓글, 메일은 언제나 환영합니다!

댓글남기기