서비스 워커로 이벤트 처리

확장 프로그램 서비스 워커 개념을 다루는 튜토리얼입니다.

개요

이 가이드에서는 Chrome 확장 프로그램 서비스 워커를 소개합니다. 그 일환으로 튜토리얼을 따라 사용자가 Chrome API 참조 문서로 빠르게 이동할 수 있는 확장 프로그램을 빌드합니다. 페이지를 찾을 수 있습니다. 다음을 수행하는 방법을 배우게 됩니다.

  • 서비스 워커를 등록하고 모듈을 가져옵니다.
  • 확장 프로그램 서비스 워커를 디버그합니다.
  • 상태 관리 및 이벤트 처리
  • 주기적인 이벤트를 트리거합니다.
  • 콘텐츠 스크립트로 소통하기

시작하기 전에

이 가이드에서는 기본적인 웹 개발 경험이 있다고 가정합니다. 자세한 내용은 확장 프로그램 101Hello World 확장 프로그램 개발에 착수했습니다.

확장 프로그램 빌드

먼저 확장 파일을 보관할 quick-api-reference라는 새 디렉터리를 만듭니다. 또는 GitHub 샘플 저장소에서 소스 코드를 다운로드합니다.

1단계: 서비스 워커 등록

프로젝트의 루트에 매니페스트 파일을 생성하고 다음 코드를 추가합니다.

manifest.json:

{
  "manifest_version": 3,
  "name": "Open extension API reference",
  "version": "1.0.0",
  "icons": {
    "16": "images/icon-16.png",
    "128": "images/icon-128.png"
  },
  "background": {
    "service_worker": "service-worker.js"
  }
}

확장 프로그램은 매니페스트에 서비스 워커를 등록하며, 이 작업은 단일 JavaScript 파일만 사용합니다. 웹페이지에서와 같이 navigator.serviceWorker.register()를 호출할 필요는 없습니다.

images 폴더를 만든 다음 그 안에 아이콘을 다운로드합니다.

매니페스트에 있는 확장 프로그램의 메타데이터아이콘에 대해 자세히 알아보려면 읽기 시간 튜토리얼의 첫 번째 단계를 확인하세요.

2단계: 여러 서비스 워커 모듈 가져오기

서비스 워커는 두 가지 특성을 구현합니다. 유지관리를 개선하기 위해 각 기능을 별도의 모듈에서 구현하겠습니다. 먼저 매니페스트에서 서비스 워커를 ES 모듈로 선언해야 합니다. 그러면 서비스 워커에서 모듈을 가져올 수 있습니다.

manifest.json:

{
 "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  },
}

service-worker.js 파일을 만들고 다음 두 모듈을 가져옵니다.

import './sw-omnibox.js';
import './sw-tips.js';

이러한 파일을 만들고 각 파일에 콘솔 로그를 추가합니다.

sw-omnibox.js:

console.log("sw-omnibox.js");

sw-tips.js:

console.log("sw-tips.js");

서비스 워커에서 여러 파일을 가져오는 다른 방법을 알아보려면 스크립트 가져오기를 참조하세요.

선택사항: 서비스 워커 디버깅

서비스 워커 로그를 찾는 방법과 로그가 언제 종료되었는지 알아보겠습니다. 먼저 안내에 따라 압축해제된 확장 프로그램을 로드합니다.

30초 후에 '서비스 워커 (비활성)'가 표시됩니다. 서비스 워커가 종료되었음을 의미합니다 '서비스 워커 (비활성)'를 클릭합니다. 링크를 사용하여 검사할 수 있습니다. 다음 애니메이션은 이를 보여줍니다.

서비스 워커를 검사하다가 깨웠다는 걸 알아채셨나요? devtools에서 서비스 워커를 열면 활성 상태로 유지됩니다. 서비스 워커가 종료될 때 확장 프로그램이 올바르게 동작하도록 하려면 DevTools를 닫아야 합니다.

이제 확장 프로그램을 중단하여 오류를 찾을 위치를 학습합니다. 이를 위한 한 가지 방법은 '.js'를 삭제하는 것입니다. service-worker.js 파일의 './sw-omnibox.js' 가져오기에서 이를 삭제합니다. Chrome에서 서비스 워커를 등록할 수 없습니다.

chrome://extensions로 돌아가서 확장 프로그램을 새로고침합니다. 다음 두 가지 오류가 표시됩니다.

Service worker registration failed. Status code: 3.

An unknown error occurred when fetching the script.

확장 프로그램 서비스 워커를 디버그하는 다른 방법은 확장 프로그램 디버깅을 참고하세요.

4단계: 상태 초기화

Chrome은 서비스 워커가 필요하지 않은 경우 이를 종료합니다. chrome.storage API를 사용하여 서비스 워커 세션 간에 상태를 유지합니다. 저장소 액세스의 경우 매니페스트에서 권한을 요청해야 합니다.

manifest.json:

{
  ...
  "permissions": ["storage"],
}

먼저 스토리지에 기본 추천을 저장합니다. 확장 프로그램이 처음 설치될 때 runtime.onInstalled() 이벤트를 수신 대기하여 상태를 초기화할 수 있습니다.

sw-omnibox.js:

...
// Save default API suggestions
chrome.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'install') {
    chrome.storage.local.set({
      apiSuggestions: ['tabs', 'storage', 'scripting']
    });
  }
});

서비스 워커는 창 객체에 직접 액세스할 수 없으므로 window.localStorage: 값을 저장합니다. 또한 서비스 워커는 수명이 짧은 실행 환경입니다. 사용자의 브라우저 세션 전반에 걸쳐 반복적으로 종료되어 전역 변수입니다 대신 로컬 머신에 데이터를 저장하는 chrome.storage.local를 사용하세요.

확장 프로그램 서비스 워커를 위한 다른 스토리지 옵션에 대해 알아보려면 전역 변수를 사용하는 대신 데이터 유지를 참조하세요.

5단계: 이벤트 등록

모든 이벤트 리스너는 서비스 워커의 전역 범위에 정적으로 등록되어야 합니다. 즉, 이벤트 리스너는 비동기 함수에 중첩되어서는 안 됩니다. 이렇게 하면 Chrome에서 서비스 워커가 재부팅되는 경우 모든 이벤트 핸들러가 복원되도록 할 수 있습니다.

이 예에서는 chrome.omnibox API를 사용하지만 먼저 매니페스트에서 검색주소창 키워드 트리거를 선언해야 합니다.

manifest.json:

{
  ...
  "minimum_chrome_version": "102",
  "omnibox": {
    "keyword": "api"
  },
}
드림

이제 스크립트 최상위 수준에서 검색주소창 이벤트 리스너를 등록합니다. 사용자가 주소 표시줄에 검색주소창 키워드 (api)를 입력한 다음 탭 또는 스페이스를 입력하면 Chrome에서 스토리지의 키워드를 기반으로 한 추천 목록을 표시합니다. 현재 사용자 입력과 suggestResult 객체를 가져오는 onInputChanged() 이벤트가 이러한 추천을 채웁니다.

sw-omnibox.js:

...
const URL_CHROME_EXTENSIONS_DOC =
  'https://developer.chrome.com/docs/extensions/reference/';
const NUMBER_OF_PREVIOUS_SEARCHES = 4;

// Display the suggestions after user starts typing
chrome.omnibox.onInputChanged.addListener(async (input, suggest) => {
  await chrome.omnibox.setDefaultSuggestion({
    description: 'Enter a Chrome API or choose from past searches'
  });
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  const suggestions = apiSuggestions.map((api) => {
    return { content: api, description: `Open chrome.${api} API` };
  });
  suggest(suggestions);
});

사용자가 추천을 선택하면 onInputEntered()에서 해당하는 Chrome API 참조 페이지가 열립니다.

sw-omnibox.js:

...
// Open the reference page of the chosen API
chrome.omnibox.onInputEntered.addListener((input) => {
  chrome.tabs.create({ url: URL_CHROME_EXTENSIONS_DOC + input });
  // Save the latest keyword
  updateHistory(input);
});

updateHistory() 함수는 검색주소창 입력을 받아 storage.local에 저장합니다. 이렇게 하면 최근 검색어를 나중에 검색주소창 추천 검색어로 사용할 수 있습니다.

sw-omnibox.js:

...
async function updateHistory(input) {
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  apiSuggestions.unshift(input);
  apiSuggestions.splice(NUMBER_OF_PREVIOUS_SEARCHES);
  return chrome.storage.local.set({ apiSuggestions });
}
드림

6단계: 반복 일정 설정

setTimeout() 또는 setInterval() 메서드는 일반적으로 지연되거나 주기적인 작업을 수행하는 데 사용됩니다. 할 수 있습니다 그러나 서비스가 종료될 때 스케줄러가 타이머를 취소하므로 이러한 API는 실패할 수 있습니다. 종료됩니다 대신 확장 프로그램에서 chrome.alarms API를 사용할 수 있습니다.

먼저 매니페스트에서 "alarms" 권한을 요청합니다. 또한 원격으로 호스팅되는 위치에서 확장 프로그램 도움말을 가져오려면 호스트 권한을 요청해야 합니다.

manifest.json:

{
  ...
  "permissions": ["storage"],
  "permissions": ["storage", "alarms"],
  "host_permissions": ["https://extension-tips.glitch.me/*"],
}

확장 프로그램이 모든 도움말을 가져와 무작위로 하나를 선택하여 저장소에 저장합니다. 팁을 업데이트하기 위해 하루에 한 번 실행되는 알람이 생성됩니다. Chrome을 종료하면 알람이 저장되지 않습니다. 따라서 알람이 있는지 확인하고 없으면 알람을 생성해야 합니다.

sw-tips.js:

// Fetch tip & save in storage
const updateTip = async () => {
  const response = await fetch('https://extension-tips.glitch.me/tips.json');
  const tips = await response.json();
  const randomIndex = Math.floor(Math.random() * tips.length);
  return chrome.storage.local.set({ tip: tips[randomIndex] });
};

const ALARM_NAME = 'tip';

// Check if alarm exists to avoid resetting the timer.
// The alarm might be removed when the browser session restarts.
async function createAlarm() {
  const alarm = await chrome.alarms.get(ALARM_NAME);
  if (typeof alarm === 'undefined') {
    chrome.alarms.create(ALARM_NAME, {
      delayInMinutes: 1,
      periodInMinutes: 1440
    });
    updateTip();
  }
}

createAlarm();

// Update tip once a day
chrome.alarms.onAlarm.addListener(updateTip);
드림

7단계: 다른 상황과 커뮤니케이션하기

확장 프로그램은 콘텐츠 스크립트를 사용하여 페이지의 콘텐츠를 읽고 수정합니다. 사용자가 Chrome API 참조 페이지를 방문하면 확장 프로그램의 콘텐츠 스크립트가 최신 정보로 페이지를 업데이트합니다. 서비스 워커에게 하루의 팁을 요청하는 메시지를 전송합니다.

먼저 매니페스트에서 콘텐츠 스크립트를 선언하고 Chrome API 참조 문서에 해당하는 일치 패턴을 추가합니다.

manifest.json:

{
  ...
  "content_scripts": [
    {
      "matches": ["https://developer.chrome.com/docs/extensions/reference/*"],
      "js": ["content.js"]
    }
  ]
}

새 콘텐츠 파일을 만듭니다. 다음 코드는 서비스 워커에 팁을 요청하는 메시지를 보냅니다. 그런 다음 확장 프로그램 팁을 포함하는 팝오버를 여는 버튼을 추가합니다. 이 코드는 새로운 웹 플랫폼인 Popover API를 사용합니다.

content.js:

(async () => {
  // Sends a message to the service worker and receives a tip in response
  const { tip } = await chrome.runtime.sendMessage({ greeting: 'tip' });

  const nav = document.querySelector('.upper-tabs > nav');
  
  const tipWidget = createDomElement(`
    <button type="button" popovertarget="tip-popover" popovertargetaction="show" style="padding: 0 12px; height: 36px;">
      <span style="display: block; font: var(--devsite-link-font,500 14px/20px var(--devsite-primary-font-family));">Tip</span>
    </button>
  `);

  const popover = createDomElement(
    `<div id='tip-popover' popover style="margin: auto;">${tip}</div>`
  );

  document.body.append(popover);
  nav.append(tipWidget);
})();

function createDomElement(html) {
  const dom = new DOMParser().parseFromString(html, 'text/html');
  return dom.body.firstElementChild;
}

마지막 단계는 일일 팁과 함께 콘텐츠 스크립트에 회신을 보내는 메시지 핸들러를 서비스 워커에 추가하는 것입니다.

sw-tips.js:

...
// Send tip to content script via messaging
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.greeting === 'tip') {
    chrome.storage.local.get('tip').then(sendResponse);
    return true;
  }
});

작동 테스트

프로젝트의 파일 구조가 다음과 같은지 확인합니다.

확장 프로그램 폴더의 콘텐츠: images 폴더, manifest.json, service-worker.js, sw-omnibox.js, sw-tips.js,
및 content.js

로컬에서 확장 프로그램 로드

개발자 모드에서 압축해제된 확장 프로그램을 로드하려면 Hello world의 단계를 따르세요.

참조 페이지 열기

  1. 키워드 'api'를 입력하세요. 를 입력합니다.
  2. 'Tab' 누르기 'space'를 입력합니다.
  3. API의 전체 이름을 입력합니다.
    • 또는 이전 검색어 목록에서 선택
  4. Chrome API 참조 페이지가 새 페이지로 열립니다.

예를 들면 다음과 같습니다.

<ph type="x-smartling-placeholder">
</ph> 런타임 API 참조를 여는 빠른 API 참조 <ph type="x-smartling-placeholder">
</ph> Runtime API를 여는 빠른 API 확장 프로그램

오늘의 도움말 열기

탐색 메뉴에 있는 도움말 버튼을 클릭하여 확장 프로그램 도움말을 엽니다.

<ph type="x-smartling-placeholder">
</ph> 일일 도움말 열기 <ph type="x-smartling-placeholder">
</ph> 빠른 API 확장 프로그램을 소개합니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.

🎊 개선 가능성

오늘 학습한 내용을 기반으로 다음 중 하나를 수행해 보세요.

  • 검색주소창 추천을 구현하는 다른 방법을 살펴보세요.
  • 확장 팁을 표시하기 위한 자체 맞춤 모달을 만듭니다.
  • MDN의 Web Extensions 참조 API 페이지에 대한 추가 페이지를 엽니다.

계속 빌드하세요!

이 튜토리얼을 완료하신 것을 축하합니다 🎉. 계속해서 다른 스킬을 완료해 스킬을 레벨업하세요 초보자 튜토리얼:

확장 학습할 내용
읽기 시간 특정 페이지 집합에 요소를 자동으로 삽입하려는 경우
탭 관리자 브라우저 탭을 관리하는 팝업을 만들기 위해
집중 모드 확장 프로그램 작업을 클릭한 후 현재 페이지에서 코드를 실행합니다.

계속 탐색하기

확장 프로그램 서비스 워커 학습 과정을 계속 진행하려면 다음 도움말을 살펴보는 것이 좋습니다.