Chrome 확장 프로그램에서 Offscreen Documents 만들기: 완전 가이드

발행: (2025년 12월 15일 오후 10:40 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

소개

Manifest V3 로 Chrome 확장 프로그램을 개발하고 있다면 서비스 워커가 자주 종료되는 문제를 겪어봤을 것입니다. Offscreen 문서는 사용자에게 보이지 않는 숨겨진 HTML 환경을 제공하여 지속적인 상태를 유지하고 연속적인 작업을 수행할 수 있게 해줍니다.

Offscreen 문서란?

Offscreen 문서는 확장 프로그램의 백그라운드에서 실행되는 숨겨진 HTML 페이지입니다. 리소스를 절약하기 위해 짧은 수명으로 동작하는 서비스 워커와 달리, Offscreen 문서는 계속 살아 있어 다음과 같은 작업을 할 수 있습니다:

  • 지속적인 데이터 구조 유지(Map, Set 등)
  • 대용량 데이터셋에 대한 무거운 연산 수행
  • 지속적인 처리가 필요한 오디오/비디오 스트림 처리

즉, 확장 프로그램이 제어하는 보이지 않는 웹 페이지이며, 장시간 실행되는 작업을 위한 안정적인 환경을 제공합니다.

이상적인 사용 사례

  • 지속적인 데이터 처리 – 서비스 워커가 중단될 때 사라지는 메모리 데이터를 유지합니다.
  • 무거운 연산 – 중단 없이 CPU 집약적인 알고리즘을 실행합니다.
  • 오디오/비디오 처리 – 메모리에 지속적으로 접근해야 하는 미디어를 다룹니다.

주요 제한 사항

  • 호스트 웹사이트의 DOM에 접근할 수 없습니다(버튼, 콘텐츠 등).
  • Chrome의 Manifest V3 확장 프로그램에서만 사용할 수 있으며 Firefox에서는 지원되지 않습니다.
  • 콘텐츠 스크립트와는 별개의 격리된 컨텍스트에서 실행됩니다.
  • 메시징을 위해 chrome.runtime API만 사용할 수 있고, storagetabs와 같은 다른 API는 사용할 수 없습니다.

Manifest 선언

manifest.jsonoffscreen 권한을 추가합니다:

{
  "manifest_version": 3,
  "name": "Your Extension Name",
  "version": "1.0.0",
  "description": "A short description of your extension.",
  "permissions": ["offscreen"],
  "background": {
    "service_worker": "background.js",
    "type": "module"
  }
}

Offscreen HTML 파일 (offscreen.html)

확장 프로그램 루트에 이 파일을 생성합니다. 사용자는 전혀 보지 못하지만 실행 환경을 제공합니다.

<!DOCTYPE html>
<html>
  <head>
    <title>Offscreen Document</title>
  </head>
  <body></body>
</html>

Offscreen 스크립트 (offscreen.js)

지속적인 작업을 처리하고 백그라운드 스크립트로부터 메시지를 수신합니다.

// Perform heavy processing or maintain persistent state
console.log('Offscreen document is live');

// Listen for messages from the background or content scripts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'Blah_Blah') {
    // Perform your processing here
    // ...

    // Example response
    sendResponse({ result: 'processed' });
  }
  // Return true to keep the message channel open for async response
  return true;
});

Note: Offscreen 콘솔 로그는 서비스 워커와 동일한 검사기에서 확인할 수 있습니다. chrome://extensions/ 페이지를 열고 개발자 모드를 활성화한 뒤 “service worker” 링크를 클릭하면 로그를 볼 수 있습니다.

Offscreen 문서 생성 (background.js)

let creatingOffscreen = null;

async function ensureOffscreen() {
  const offscreenUrl = chrome.runtime.getURL('offscreen.html');

  // Check if an offscreen document already exists
  if ('getContexts' in chrome.runtime) {
    const contexts = await chrome.runtime.getContexts({
      contextTypes: ['OFFSCREEN_DOCUMENT'],
      documentUrls: [offscreenUrl],
    });

    if (contexts.length > 0) {
      console.log('Offscreen document already exists');
      return;
    }
  } else {
    // Fallback for older Chrome versions
    const clients = await self.clients.matchAll();
    if (clients.some(client => client.url.includes(chrome.runtime.id))) {
      return;
    }
  }

  // Create the offscreen document if it doesn't exist
  if (!creatingOffscreen) {
    creatingOffscreen = chrome.offscreen.createDocument({
      url: 'offscreen.html',
      reasons: ['WORKERS'],
      justification: 'Maintain persistent data structures for extension functionality',
    });

    await creatingOffscreen;
    creatingOffscreen = null;
    console.log('Offscreen document created successfully');
  } else {
    // Wait for an ongoing creation to finish
    await creatingOffscreen;
  }
}

// Ensure offscreen document on startup
chrome.runtime.onStartup.addListener(() => {
  ensureOffscreen();
  console.log('Extension started, offscreen document ensured');
});

// Ensure offscreen document on installation
chrome.runtime.onInstalled.addListener(() => {
  ensureOffscreen();
  console.log('Extension installed, offscreen document created');
});

// Handle explicit requests to ensure the offscreen document exists
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg && msg.type === 'ensure_offscreen') {
    ensureOffscreen()
      .then(() => sendResponse({ ok: true }))
      .catch(err => sendResponse({ ok: false, error: String(err) }));
    return true; // Keep channel open for async response
  }
});

ensureOffscreen 작동 방식

  1. 존재 여부 확인chrome.runtime.getContexts()(권장) 또는 self.clients.matchAll()(폴백)을 사용합니다.
  2. 중복 생성 방지 – 공유된 creatingOffscreen 프라미스로 레이스 컨디션을 방지합니다.
  3. 필요 시 생성chrome.offscreen.createDocument()를 호출해 url, reasons, justification을 전달합니다.

Offscreen 생성 파라미터

  • url – Offscreen HTML 파일의 경로.
  • reasons – Chrome API에서 허용하는 이유 중 하나 이상(WORKERS, AUDIO_PLAYBACK, DOM_PARSER 등).
  • justification – 문서가 필요한 명확한 이유; 확장 프로그램 제출 시 검토됩니다.

Offscreen 문서 닫기

문서가 더 이상 필요 없을 때는 닫아 리소스를 해제할 수 있습니다:

async function closeOffscreen() {
  try {
    await chrome.offscreen.closeDocument();
    console.log('Offscreen document closed');
  } catch (error) {
    console.error('Error closing offscreen document:', error);
  }
}

백그라운드와 Offscreen 간 메시징

Chrome runtime 메시징 API를 사용합니다.

백그라운드 → Offscreen:

chrome.runtime.sendMessage(
  { type: 'Blah_Blah' },
  response => {
    console.log('Response from offscreen:', response);
  }
);

브라우저 지원 및 특성

  • Manifest 버전 – Offscreen 문서는 Manifest V3(Chrome 및 기타 Chromium 기반 브라우저)에서만 사용할 수 있습니다. Firefox에서는 지원되지 않습니다.
  • DOM 접근 – 호스트 페이지의 DOM에 직접 접근할 수 없으며, 모든 데이터는 메시지를 통해 전달해야 합니다.
  • 지속성 – 서비스 워커와 달리 작업 간 상태가 유지됩니다.
  • 가시성 – 사용자에게 전혀 보이지 않으며 UI 요소가 표시되지 않습니다.

추가 자료

  • Offscreen 문서에 대한 공식 Chrome 문서.
  • 인증, 구독 처리 등을 포함한 준비된 확장 프로그램 설정을 확인하려면 extFast boilerplate를 살펴보세요.
Back to Blog

관련 글

더 보기 »