Declared vs optional permission — 두 grant 순간을 실용으로
~12 min · permissions, optional_permissions, chrome.permissions, user-gesture
Level 0Extension 입덕
0 XP0/54 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Declared permission 은 입장료. Optional permission 은 나중에 파는 upgrade. Lesson 2 가 chrome.permissions API 가 요청 defer 하고, feature gate 하고, '아니' 에서 복구하게 해 주는 법."
chrome.permissions API
네 method 가 runtime 이야기 cover:
chrome.permissions.contains({ permissions?, origins? }) — extension 이 현재 이것들 가졌어? Boolean 반환. 저렴, sync 느낌 (Promise-based 지만 즉시 resolve).
chrome.permissions.request({ permissions?, origins? }) — user 한테 지금 이것들 grant 요청. user-gesture handler 안에서 호출 필수. Grant 면 true, deny 나 dismiss 면 false 반환.
chrome.permissions.remove({ permissions?, origins? }) — 이전에 가졌던 permission drop. User 가 이전에 grant 한 upgrade 회수하게 해 주는 데 유용.
User-gesture-derive 된 popup click 으로부터의 message (transitive).
Timer, alarm, tabs event, 다른 non-gesture context 에서 호출 시 This function must be called during a user gesture. 로 reject. Fake 시도 안 함 — rejection 은 보안 feature, 버그 아님.
Two-step UX 패턴
Optional-permission-gated feature 의 깔끔한 shape:
Feature 의 UI 정상 렌더 (버튼 / menu item / link).
Click 시 먼저 chrome.permissions.contains 확인. 이미 grant 면 feature 실행.
Grant 안 됐으면 chrome.permissions.request 호출. Chrome prompt 나타남.
User 가 grant 하면 feature 실행 진행.
User 가 deny 하면 친절한 inline message — "ClipDeck 이 export 위해 Downloads permission 필요. Grant 위해 재시도." — 표시하고 feature 버튼 visible 유지.
이 shape 이 대칭적이고 용서적. User 가 언제든 회수 가능 (Chrome 이 chrome://extensions/?id=... 에 Permission 탭 표시), 같은 flow 다시 trigger 해 re-grant.
Request 가 사는 곳
Handler 가 user-gesture access 가진 어디든 가능. 세 흔한 home:
Popup 버튼 — popup.js click handler. Popup 이 대부분 chrome.permissions.request flow 에 auto-close, grant 후 계속해야 하면 popup 다시 열기.
Options page 버튼 — 보통 opt-in feature 구성의 home. Settings page 가 request 동안 화면에 persist, 가장 부드러운 UX.
Side panel 버튼 — popup 과 같게 동작하지만 prompt 후 열려 있음.
SW 가 request 직접 시작 못 함 (SW 에 user gesture 없음), 하지만 그 surface 중 하나에서 메시지 받고 surface 가 grant 확인 후 downstream work trigger CAN.
Optional host 생김새
optional_host_permissions 가 URL pattern 에 같은 방식 동작. ClipDeck 이 install 시 user 가 가입 안 한 site 에 inject 필요할 때 사용. 예: popup 의 "이 site 에서 ClipDeck 허용" 버튼이 https://<current-host>/* 요청. Chrome 이 "ClipDeck 이 이 site 의 데이터 read 와 change 허용?" dialog 표시. User 가 Allow click 후부터 content script 가 거기 auto-inject.
Declared = essential, install 시 지불. Optional = nice-to-have, user 가 손 뻗을 때 지불. chrome.permissions.contains + chrome.permissions.request + graceful 'deny' 경로 가 full UX.
Request 시 popup-close 함정. 많은 Chrome 에서 permission prompt 가 popup 닫음. Popup flow 가 "click → request → run feature" 였으면, 'run feature' 코드 경로 실행 안 됨 — popup 사라짐. 실제 feature work 를 SW (popup 이 grant 후 메시지) 나 side panel (열린 채 유지) 로 옮겨 post-grant action 닿게.
Code
popup.js — permission 확보 후 SW 에 work 위임·javascript
// popup.js — chrome.permissions.request 로 feature-gated
async function ensureDownloadsPermission() {
const has = await chrome.permissions.contains({ permissions: ["downloads"] });
if (has) return true;
return chrome.permissions.request({ permissions: ["downloads"] });
}
document.getElementById("exportBtn").addEventListener("click", async () => {
const ok = await ensureDownloadsPermission();
if (!ok) {
document.getElementById("exportStatus").textContent =
"Downloads permission denied. Click Export again to retry.";
return;
}
// 실제 export trigger. Popup 이 prompt 에 닫힐 수 있어, 더 안전한 패턴은
// SW 한테 메시지 보내고 다운로드 소유하게.
await chrome.runtime.sendMessage({ type: "exportClips" });
document.getElementById("exportStatus").textContent = "Exporting…";
});
background.js — popup 이 grant 확인 후 SW 가 chrome.downloads 소유·javascript
background.js — live UI 반응성 위해 onAdded / onRemoved listen·javascript
// background.js — user 가 어떤 permission grant 나 revoke 할 때 반응
chrome.permissions.onAdded.addListener((perms) => {
console.log("[ClipDeck SW] permissions granted:", perms.permissions, perms.origins);
});
chrome.permissions.onRemoved.addListener((perms) => {
console.log("[ClipDeck SW] permissions revoked:", perms.permissions, perms.origins);
// 예: downloads 가 revoke 됐으면 popup 의 Export 버튼 disable
// (popup 이 매 open 시 chrome.permissions.contains 어쨌든 re-read 함).
});
clipdeck/popup.html 에 id exportBtn 의 Export Clips 버튼과 status div exportStatus 추가. 첫 번째 code block 을 clipdeck/popup.js 에, 두 번째를 clipdeck/background.js 에 추가. Reload. Popup 열기, Export Clips click. Chrome 이 'Add Downloads' prompt 표시. Allow click — clip export 의 Save dialog 나타남. Popup 다시 열기; permission 이 이제 grant 됐으니, 두 번째 export 는 one click. 다음 chrome://extensions/?id= → Permissions 탭, Downloads revoke, 다시 시도 — prompt 다시 나타남. Deny 시 popup status 메시지 보이는지 확인.
Hint
chrome.permissions.request 가 This function must be called during a user gesture 로 reject 하면, user gesture context 잃은 것 — 보통 request 호출 전 다른 거 await 해서. Handler 에서 request 를 먼저 호출, 다른 work 는 그 후. chrome.downloads.download 가 prompt 가 Allow 보여줬는데도 permission not granted error 면 SW 의 permission snapshot 이 stale — SW handler 시작에서 chrome.permissions.contains 다시 호출하면 현재 grant read. Popup-close 함정이 여기 물어: popup.js 의 request 뒤에 'continue export' logic 두지 안 함; 보인 대로 SW 로 옮기기.
Progress
Progress is local-only — sign in to sync across devices.