Chrome 확장 프로그램에서 위치정보를 가져오려면 일반적으로 웹사이트에서 사용하는 것과 동일한 navigator.geolocation
웹 플랫폼 API를 사용합니다. Chrome 확장 프로그램에서는 민감한 정보에 액세스하는 권한을 웹사이트와 다르게 처리하므로 이 도움말을 참고하세요. 위치정보는 매우 민감한 데이터이므로 브라우저는 사용자가 정확한 위치가 공유되는 시기와 위치를 완전히 인식하고 제어할 수 있도록 보장합니다.
MV3 확장 프로그램에서 위치정보 사용
웹에서 브라우저는 특정 출처에 사용자 위치에 액세스할 수 있는 권한을 부여하라는 메시지를 표시하여 사용자의 위치정보 데이터를 보호합니다. 동일한 권한 모델이 확장 프로그램에 항상 적합한 것은 아닙니다.
권한에만 차이가 없습니다. 위에서 언급했듯이 navigator.geolocation
는 DOM API입니다. 즉, 웹사이트를 구성하는 API의 일부입니다. 따라서 매니페스트 v3 확장 프로그램의 백본인 확장 프로그램 서비스 워커와 같은 worker 컨텍스트 내에서는 액세스할 수 없습니다. 하지만 geolocation
를 사용할 수는 있습니다. 단지 어디에서 어떻게 사용하는지에 차이가 있을 뿐입니다.
서비스 워커에서 위치정보 사용
서비스 워커 내에는 navigator
객체가 없습니다. 페이지의 document
객체에 액세스할 수 있는 컨텍스트 내에서만 사용할 수 있습니다. 서비스 워커 내에서 액세스하려면 Offscreen Document
를 사용합니다. 이는 확장 프로그램과 함께 번들로 묶을 수 있는 HTML 파일에 대한 액세스를 제공합니다.
시작하려면 매니페스트의 "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"
에 추가해도 사용자의 위치정보에 자동으로 액세스할 수 있는 것은 아닙니다. 콘텐츠 스크립트 샘플에서 확인할 수 있습니다.