위치정보 사용

Chrome 확장 프로그램에서 위치정보를 가져오려면 웹사이트에서 일반적으로 사용하는 것과 동일한 navigator.geolocation 웹 플랫폼 API를 사용하세요. 이 도움말은 Chrome 확장 프로그램이 민감한 정보에 대한 액세스 권한을 웹사이트와 다른 방식으로 처리하기 때문에 표시되었습니다. 위치정보는 매우 민감한 정보이므로 브라우저를 통해 사용자는 정확한 위치가 공유되는 시기와 장소를 완전히 파악하고 제어할 수 있습니다.

MV3 확장 프로그램에서 위치정보 사용

웹에서 브라우저는 사용자가 특정 출처의 위치 정보 액세스 권한을 부여해 달라는 프롬프트가 표시되어 위치정보 데이터를 활용할 수 있습니다. 동일한 권한 모델이 항상 확장 프로그램에 적합한 것은 아닙니다.

<ph type="x-smartling-placeholder">
</ph> 웹사이트에서 Geolocation API에 대한 액세스를 요청할 때 표시되는 권한 메시지의 스크린샷
위치정보 권한 메시지

권한만이 다른 것은 아닙니다. 위에서 언급했듯이 navigator.geolocationDOM API로, 웹사이트를 구성하는 API의 일부입니다. 따라서 매니페스트 v3 확장 프로그램의 백본인 확장 프로그램 서비스 워커와 같은 작업자 컨텍스트 내에서 액세스할 수 없습니다. 하지만 geolocation는 계속 사용할 수 있습니다. 사용하는 방법과 위치에 따라 미묘한 차이가 있을 뿐입니다.

서비스 워커에서 위치정보 사용

서비스 워커 내부에는 navigator 객체가 없습니다. 페이지의 document 객체에 액세스할 수 있는 컨텍스트 내에서만 사용할 수 있습니다. 서비스 워커 내에서 액세스 권한을 얻으려면 확장 프로그램과 번들로 묶을 수 있는 HTML 파일에 대한 액세스 권한을 제공하는 Offscreen Document를 사용합니다.

먼저 매니페스트의 "permissions" 섹션에 "offscreen"를 추가합니다.

manifest.json:

{
  "name": "My extension",
    ...
  "permissions": [
    ...
   "offscreen"
  ],
  ...
}

"offscreen" 권한을 추가한 후 오프스크린 문서가 포함된 HTML 파일을 확장 프로그램에 추가합니다. 이 사례는 페이지의 콘텐츠를 사용하지 않으므로 거의 빈 파일일 수 있습니다. 스크립트에서 로드되는 작은 HTML 파일만 하면 됩니다.

offscreen.html:

<!doctype html>
<title>offscreenDocument</title>
<script src="offscreen.js"></script>

이 파일을 프로젝트의 루트에 offscreen.html로 저장합니다.

앞서 언급했듯이 offscreen.js라는 스크립트가 필요합니다. 또한 이를 확장 프로그램과 번들로 묶어야 합니다. 서비스 워커의 위치정보 정보 소스가 됩니다. 애플리케이션과 서비스 워커 간에 메시지를 전달할 수 있습니다.

offscreen.js:

chrome.runtime.onMessage.addListener(handleMessages);
function handleMessages(message, sender, sendResponse) {
  // Return early if this message isn't meant for the offscreen document.
  if (message.target !== 'offscreen') {
    return;
  }

  if (message.type !== 'get-geolocation') {
    console.warn(`Unexpected message type received: '${message.type}'.`);
    return;
  }

  // You can directly respond to the message from the service worker with the
  // provided `sendResponse()` callback. But in order to be able to send an async
  // response, you need to explicitly return `true` in the onMessage handler
  // As a result, you can't use async/await here. You'd implicitly return a Promise.
  getLocation().then((loc) => sendResponse(loc));

  return true;
}

// getCurrentPosition() returns a prototype-based object, so the properties
// end up being stripped off when sent to the service worker. To get
// around this, create a deep clone.
function clone(obj) {
  const copy = {};
  // Return the value of any non true object (typeof(null) is "object") directly.
  // null will throw an error if you try to for/in it. Just return
  // the value early.
  if (obj === null || !(obj instanceof Object)) {
    return obj;
  } else {
    for (const p in obj) {
      copy[p] = clone(obj[p]);
    }
  }
  return copy;
}

async function getLocation() {
  // Use a raw Promise here so you can pass `resolve` and `reject` into the
  // callbacks for getCurrentPosition().
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(
      (loc) => resolve(clone(loc)),
      // in case the user doesnt have/is blocking `geolocation`
      (err) => reject(err)
    );
  });
}

이를 제자리에 설정했으므로 이제 서비스 워커에서 화면 밖 문서에 액세스할 수 있습니다.

chrome.offscreen.createDocument({
  url: 'offscreen.html',
  reasons: [chrome.offscreen.Reason.GEOLOCATION || chrome.offscreen.Reason.DOM_SCRAPING],
  justification: 'geolocation access',
});

화면 밖 문서에 액세스하는 경우 reason를 포함해야 합니다. geolocation 이유는 원래 사용할 수 없었으므로 DOM_SCRAPING의 대체를 지정하고 justification 섹션에 코드가 실제로 수행하는 작업을 설명합니다. 이 정보는 Chrome 웹 스토어의 검토 과정에서 화면 밖 문서가 유효한 목적으로 사용되고 있는지 확인하는 데 사용됩니다.

화면 밖 문서에 대한 참조가 있으면 해당 문서를 보내 업데이트된 위치정보 정보를 제공하도록 요청할 수 있습니다.

service_worker.js:

const OFFSCREEN_DOCUMENT_PATH = '/offscreen.html';
let creating; // A global promise to avoid concurrency issues

chrome.runtime.onMessage.addListener(handleMessages);

async function getGeolocation() {
  await setupOffscreenDocument(OFFSCREEN_DOCUMENT_PATH);
  const geolocation = await chrome.runtime.sendMessage({
    type: 'get-geolocation',
    target: 'offscreen'
  });
  await closeOffscreenDocument();
  return geolocation;
}

async function hasDocument() {
  // Check all windows controlled by the service worker to see if one
  // of them is the offscreen document with the given path
  const offscreenUrl = chrome.runtime.getURL(OFFSCREEN_DOCUMENT_PATH);
  const matchedClients = await clients.matchAll();

  return matchedClients.some(c => c.url === offscreenUrl)
}

async function setupOffscreenDocument(path) {
  //if we do not have a document, we are already setup and can skip
  if (!(await hasDocument())) {
    // create offscreen document
    if (creating) {
      await creating;
    } else {
      creating = chrome.offscreen.createDocument({
        url: path,
        reasons: [chrome.offscreen.Reason.GEOLOCATION || chrome.offscreen.Reason.DOM_SCRAPING],
        justification: 'add justification for geolocation use here',
      });

      await creating;
      creating = null;
    }
  }
}

async function closeOffscreenDocument() {
  if (!(await hasDocument())) {
    return;
  }
  await chrome.offscreen.closeDocument();
}

따라서 서비스 워커에서 위치정보를 가져오려고 할 때마다 다음을 호출하기만 하면 됩니다.

const location = await getGeolocation()

팝업 또는 측면 패널에서 위치정보 사용

팝업 또는 측면 패널 내에서 위치정보를 사용하는 것은 매우 간단합니다. 팝업 및 측면 패널은 웹 문서이므로 일반 DOM API에 액세스할 수 있습니다. navigator.geolocation에 직접 액세스할 수 있습니다. 표준 웹사이트와의 유일한 차이점은 manifest.json "permission" 필드를 사용하여 "geolocation" 권한을 요청해야 한다는 것입니다. 권한을 포함하지 않아도 navigator.geolocation에 계속 액세스할 수 있습니다. 하지만 이를 사용하려고 하면 사용자가 요청을 거부한 것과 마찬가지로 즉시 오류가 발생합니다. 팝업 샘플에서 이를 확인할 수 있습니다.

콘텐츠 스크립트에서 위치정보 사용

팝업과 마찬가지로 콘텐츠 스크립트에는 DOM API에 대한 전체 액세스 권한이 있습니다. 하지만 사용자는 일반적인 사용자 권한 흐름을 거치게 됩니다. 즉, "geolocation""permissions"에 추가해도 사용자의 액세스 권한이 자동으로 부여되지 않습니다. 지리적 위치 정보입니다. 콘텐츠 스크립트 샘플에서 이를 확인할 수 있습니다.