모든 페이지에서 스크립트 실행

페이지에 새 요소를 삽입하는 첫 번째 확장 프로그램을 만듭니다.

개요

이 튜토리얼에서는 Chrome 확장 프로그램 및 Chrome 웹 스토어 문서 페이지에 예상 읽기 시간을 추가하는 확장 프로그램을 빌드합니다.

확장 프로그램의 시작 페이지에 있는 독서 시간 확장
확장 프로그램의 시작 페이지에 있는 독서 시간 확장 프로그램

이 가이드에서는 다음 개념을 설명합니다.

  • 확장 프로그램 매니페스트입니다.
  • 확장 프로그램에서 사용하는 아이콘 크기입니다.
  • 콘텐츠 스크립트를 사용하여 페이지에 코드를 삽입하는 방법
  • 일치 패턴을 사용하는 방법
  • 확장 프로그램 권한

시작하기 전에

이 가이드에서는 기본적인 웹 개발 경험이 있다고 가정합니다. 확장 프로그램 개발 워크플로를 소개하는 Hello world 튜토리얼을 확인해 보세요.

확장 프로그램 빌드

먼저 확장 프로그램의 파일을 보관할 reading-time라는 새 디렉터리를 만듭니다. 원하는 경우 GitHub에서 전체 소스 코드를 다운로드할 수 있습니다.

1단계: 확장 프로그램에 대한 정보 추가

매니페스트 JSON 파일만 필수 파일입니다. 확장 프로그램에 관한 중요한 정보를 보유합니다. 프로젝트의 루트manifest.json 파일을 만들고 다음 코드를 추가합니다.

{
  "manifest_version": 3,
  "name": "Reading time",
  "version": "1.0",
  "description": "Add the reading time to Chrome Extension documentation articles"
}

이러한 키에는 확장 프로그램의 기본 메타데이터가 포함됩니다. 확장 프로그램 페이지 및 게시된 경우 Chrome 웹 스토어에 확장 프로그램이 표시되는 방식을 제어합니다. 자세히 알아보려면 매니페스트 개요 페이지에서 "name", "version", "description" 키를 확인하세요.

💡 확장 프로그램 매니페스트에 관한 기타 정보

  • 프로젝트의 루트에 있어야 합니다.
  • 유일하게 필요한 키는 "manifest_version", "name", "version"입니다.
  • 개발 중에는 주석 (//)을 지원하지만 Chrome 웹 스토어에 코드를 업로드하기 전에 주석을 삭제해야 합니다.

2단계: 아이콘 제공

그렇다면 아이콘이 필요한 이유는 무엇인가요? 아이콘은 개발 중에 선택사항이지만 Chrome 웹 스토어에서 확장 프로그램을 배포하려는 경우 필요합니다. 확장 프로그램 관리 페이지와 같은 다른 위치에도 표시됩니다.

images 폴더를 만들고 아이콘을 넣습니다. GitHub에서 아이콘을 다운로드할 수 있습니다. 다음으로 매니페스트에 강조 표시된 코드를 추가하여 아이콘을 선언합니다.

{
  "icons": {
    "16": "images/icon-16.png",
    "32": "images/icon-32.png",
    "48": "images/icon-48.png",
    "128": "images/icon-128.png"
  }
}

PNG 파일을 사용하는 것이 좋지만 SVG 파일을 제외한 다른 파일 형식도 허용됩니다.

💡 크기가 다른 아이콘은 어디에 표시되나요?

아이콘 크기 아이콘 사용
16x16 확장 프로그램 페이지 및 컨텍스트 메뉴의 favicon
32x32 Windows 컴퓨터에는 이 크기가 필요한 경우가 많습니다.
48x48 확장 프로그램 페이지에 표시됩니다.
128x128 설치 시 및 Chrome 웹 스토어에 표시됩니다.

3단계: 콘텐츠 스크립트 선언

확장 프로그램은 페이지의 콘텐츠를 읽고 수정하는 스크립트를 실행할 수 있습니다. 이를 콘텐츠 스크립트라고 합니다. 격리된 환경에 있으므로 호스트 페이지나 다른 확장 프로그램의 콘텐츠 스크립트와 충돌하지 않고 JavaScript 환경을 변경할 수 있습니다.

manifest.json에 다음 코드를 추가하여 content.js라는 콘텐츠 스크립트를 등록합니다.

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

"matches" 필드에는 하나 이상의 일치 패턴이 있을 수 있습니다. 이를 통해 브라우저는 콘텐츠 스크립트를 삽입할 사이트를 식별할 수 있습니다. 일치 패턴은 <scheme>://<host><path>의 세 부분으로 구성됩니다. '*' 문자를 포함할 수 있습니다.

💡 이 확장 프로그램에 권한 경고가 표시되나요?

사용자가 확장 프로그램을 설치하면 브라우저는 확장 프로그램의 기능을 사용자에게 알립니다. 콘텐츠 스크립트는 일치 패턴 기준을 충족하는 사이트에서 실행할 권한을 요청합니다.

이 예시에서는 사용자에게 다음과 같은 권한 경고가 표시됩니다.

사용자가 독서 시간 확장 프로그램을 설치할 때 표시되는 권한 경고
독서 시간 권한 경고

확장 프로그램 권한에 대해 자세히 알아보려면 권한 선언 및 사용자에게 경고를 참고하세요.

4단계: 독서 시간 계산 및 삽입

콘텐츠 스크립트는 표준 문서 객체 모델 (DOM)을 사용하여 페이지의 콘텐츠를 읽고 변경할 수 있습니다. 확장 프로그램은 먼저 페이지에 <article> 요소가 포함되어 있는지 확인합니다. 그런 다음 이 요소 내의 모든 단어를 계산하고 총 읽기 시간을 표시하는 단락을 만듭니다.

scripts 폴더 내에 content.js라는 파일을 만들고 다음 코드를 추가합니다.

function renderReadingTime(article) {
  // If we weren't provided an article, we don't need to render anything.
  if (!article) {
    return;
  }

  const text = article.textContent;
  const wordMatchRegExp = /[^\s]+/g; // Regular expression
  const words = text.matchAll(wordMatchRegExp);
  // matchAll returns an iterator, convert to array to get word count
  const wordCount = [...words].length;
  const readingTime = Math.round(wordCount / 200);
  const badge = document.createElement("p");
  // Use the same styling as the publish information in an article's header
  badge.classList.add("color-secondary-text", "type--caption");
  badge.textContent = `⏱️ ${readingTime} min read`;

  // Support for API reference docs
  const heading = article.querySelector("h1");
  // Support for article docs with date
  const date = article.querySelector("time")?.parentNode;

  (date ?? heading).insertAdjacentElement("afterend", badge);
}

renderReadingTime(document.querySelector("article"));

💡 이 코드에서 사용된 흥미로운 JavaScript

  • <article> 요소 내의 단어만 집계하는 데 사용되는 정규 표현식입니다.
  • insertAdjacentElement(): 요소 뒤에 읽기 시간 노드를 삽입하는 데 사용됩니다.
  • classList 속성은 요소 클래스 속성에 CSS 클래스 이름을 추가하는 데 사용됩니다.
  • 정의되지 않았거나 null일 수 있는 객체 속성에 액세스하는 데 사용되는 선택적 체이닝
  • nullish coalescing<date>이 null이거나 정의되지 않은 경우 <heading>를 반환합니다.

5단계: 변경사항 수신 대기

현재 코드에서는 왼쪽 탐색을 사용하여 기사를 전환하면 새로운 기사에 읽기 시간이 추가되지 않습니다. 이는 사이트가 History API를 사용하여 조용히 탐색하는 단일 페이지 애플리케이션 (SPA)으로 구현되어 있기 때문입니다.

이를 해결하려면 MutationObserver를 사용하여 변경사항을 수신 대기하고 새 기사에 독서 시간을 추가하면 됩니다.

이렇게 하려면 content.js 하단에 다음을 추가합니다.

const observer = new MutationObserver((mutations) => {
  for (const mutation of mutations) {
    // If a new article was added.
    for (const node of mutation.addedNodes) {
      if (node instanceof Element && node.tagName === 'ARTICLE') {
        // Render the reading time for this particular article.
        renderReadingTime(node);
      }
    }
  }
});

// https://developer.chrome.com/ is a SPA (Single Page Application) so can
// update the address bar and render new content without reloading. Our content
// script won't be reinjected when this happens, so we need to watch for
// changes to the content.
observer.observe(document.querySelector('devsite-content'), {
  childList: true
});

작동 여부 테스트

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

독서 시간 폴더의 콘텐츠: 스크립트 폴더의 manifest.json, content.js, 이미지 폴더

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

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

확장 프로그램 또는 Chrome 웹 스토어 문서 열기

다음은 각 기사를 읽는 데 걸리는 시간을 확인할 수 있는 페이지입니다.

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

시작 페이지에서 실행되는 읽기 시간
독서 시간 확장 프로그램이 포함된 확장 프로그램 시작 페이지

🎯 잠재적 개선사항

오늘 배운 내용을 바탕으로 다음 중 하나를 구현해 보세요.

  • manifest.json에 다른 일치 패턴을 추가하여 Chrome DevTools 또는 Workbox와 같은 다른 Chrome 개발자 페이지를 지원합니다.
  • 즐겨찾는 블로그 또는 문서 사이트의 읽기 시간을 계산하는 새 콘텐츠 스크립트를 추가합니다.

계속 빌드

이 튜토리얼을 완료하신 것을 축하합니다 🎉. 이 시리즈의 다른 튜토리얼을 완료하여 실력을 계속 키워 보세요.

확장 프로그램 학습할 내용
포커스 모드 확장 프로그램 작업을 클릭한 후 현재 페이지에서 코드를 실행합니다.
탭 관리자 브라우저 탭을 관리하는 팝업을 만듭니다.

계속 살펴보기

이 Chrome 확장 프로그램을 빌드하는 과정을 즐기셨기를 바라며 Chrome 개발 학습 여정을 계속 이어가시기 바랍니다. 다음 학습 과정을 권장합니다.

  • 개발자 가이드에는 고급 확장 프로그램 만들기와 관련된 문서에 대한 추가 링크가 수십 개 있습니다.
  • 확장 프로그램은 오픈 웹에서 제공되는 것 이상의 강력한 API에 액세스할 수 있습니다. Chrome API 문서에서 각 API를 살펴보세요.