~11 min · activeTab, permissions, narrow-permissions, ux
Level 0Extension 입덕
0 XP0/54 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Chrome 의 most-installed extension 의 절반이 all-websites 경고 절대 안 받음 — activeTab 과 같은 문제 푸는 narrow permission 한 줌 사용하니까. Lesson 4 가 단축 list — <all_urls> 대신 손 뻗을 거."
activeTab — 일꾼
"activeTab" 이 user 가 이런 거 통해 extension 부르는 순간 시작하는 현재-active tab 의 임시 host access 부여하는 특별 API permission:
Toolbar icon click (chrome.action.onClicked 나 popup open).
키보드 command (chrome.commands.onCommand).
Context menu (chrome.contextMenus.onClicked).
Grant 가 cover: 그 tab 의 chrome.scripting.executeScript, 그 tab 의 chrome.tabs.captureVisibleTab, tab.url / tab.title read. User 가 tab 을 새 URL 로 navigate 하거나 tab 닫을 때까지 지속.
결정적: install 경고 없음. activeTab 자체가 prompt 에 아무것도 추가 안 함. User-gesture trigger 패턴과 결합 시 all-sites alarm 없이 "on demand 동작, 요청할 때만" semantics 얻음.
activeTab vs Programmatic Injection
혼란 포인트: activeTab 이 host access 부여; scripting 이 API 부여. 보통 둘 다 원함:
"permissions": ["activeTab", "scripting"]
그 둘 선언하면 SW 가 gesture handler 안에서 user-active tab 의 chrome.scripting.executeScript 호출 가능. host_permissions 필요 없음. 항상-on injection 도 원하지 안 한 한 content-script 선언 필요 없음.
알 가치 있는 다른 narrow permission
tabGroups — Chrome 의 tab grouping read / 수정. 자체 install 경고 없음.
notifications — chrome.notifications.create 통해 데스크톱 notification 표시. Mild install 경고 ("Show notifications"); 대부분 user 한테 무해.
alarms — one-shot 이나 recurring SW wake-up 스케줄. Install 경고 없음. setInterval/setTimeout (SW 와 함께 죽음) 대체.
identity — Google sign-in 의 OAuth flow. Install 경고가 host access 가 아닌 나중 요청할 scope 요약.
webNavigation — 세밀한 navigation event. "Read your browsing history" 경고 동반; tabs 가 덜 무섭게 대부분 같은 ground cover.
contextMenus — 이미 manifest 에. 경고 없음.
storage, commands, sidePanel, omnibox, action — 다 silent.
조심해서 다룰 permission
이것들 각자 자체 시끄러운 경고 추가. 정말 필요할 때만 사용, optional_permissions 뒤 gating 고려:
history — "Read and modify your browsing history." 드물게 필요; tabs + per-tab cache 가 보통 use case cover.
cookies — "Read and modify cookies on all sites." Clip-saving extension 엔 거의 필요 없음.
geolocation — "Detect your physical location." User 놀라게 함; runtime 에 요청.
nativeMessaging — "Communicate with cooperating native applications." 데스크톱 binary 와 대화 가능; 공격 surface 열음.
debugger — "Use the developer tools on any tab." Power-user only; install 경고 시끄럽고 정당.
webRequest — "Block or modify network requests." MV3 가 MV2 대비 이거 무겁게 제한; 좁은 사용도 경고 발생.
대체 게임
Host permission 이나 무서운 API permission 선언 전 물어 봐: "더 narrow 한 방법 있어?" 보통 있음:
주기적 background job 필요? setInterval + persistent worker 아닌 alarms.
Per-site 선호 기억 필요? cookies 아닌 storage.
현재 page read 필요? host_permissions 아닌 activeTab + scripting.
User 가 방금 방문한 site 알기 필요? history 나 webNavigation 아닌 onUpdated 가진 tabs.
User 가 타이핑할 때 동작 필요? 단축키엔 commands, 주소창 keyword 엔 omnibox. debugger 안 됨.
activeTab + scripting + storage 가 실제 extension 의 큰 fraction cover 하는 no-warning trinity. Narrow permission 먼저 손 뻗기; 더 narrow 한 경로 없을 때만 host_permissions 선언.
activeTab 이 content_scripts.matches 와 상호작용하는 법. activeTab 이 선언된 content script 를 auto-inject 안 함 — 그냥 chrome.scripting 과 tab.url access 해금. activeTab 부를 때 content script load 원하면, gesture handler 에서 chrome.scripting.executeScript 호출 필수. Static content_scripts manifest entry 가 매칭 URL 에 항상-on 이고 activeTab 과 따로 content_scripts.matches 가 govern.
Code
background.js — activeTab 이 user-click tab 의 scripting 해금·javascript
background.js — 주기적 SW work 위해 chrome.alarms 가 setInterval 대체·javascript
// background.js — SW 살려 두지 안 고 주기적 work 위한 alarms
chrome.runtime.onInstalled.addListener(() => {
chrome.alarms.create("clipdeck-cleanup", {
when: Date.now() + 60_000, // 1 분 후 첫 실행
periodInMinutes: 60, // 그 다음 매 시간
});
});
chrome.alarms.onAlarm.addListener(async (alarm) => {
if (alarm.name !== "clipdeck-cleanup") return;
// pausedTabs (Track 5) 에서 stale tab id 정리
const { pausedTabs = [] } = await chrome.storage.local.get("pausedTabs");
const tabs = await chrome.tabs.query({});
const live = new Set(tabs.map((t) => t.id));
const trimmed = pausedTabs.filter((id) => live.has(id));
if (trimmed.length !== pausedTabs.length) {
await chrome.storage.local.set({ pausedTabs: trimmed });
}
});
clipdeck/manifest.json 을 세 번째 code block 으로 update — "alarms" 를 permissions 에 추가, host_permissions 비워 두기, 넓은 URL access 를 optional_host_permissions 로 옮기기. Reload. chrome://extensions → ClipDeck → Details 에서 install 경고가 이제 'Read your browsing history' (tabs 에서) 와 'Read and change your data on sites you visit' (content_scripts.matches 가 여전히 <all_urls> 라서) 로 보임 — Lesson 3 에서 content_scripts.matches 좁혔으면 'all websites' 보다 narrow, 아니면 여전히 시끄러움. 두 번째 code block (alarms cleanup) 을 background.js 에 추가. Clip 몇 개 저장, 그 tab 중 일부를 un-pause 안 하고 닫기, 한 시간 기다리기 (또는 테스트 위해 alarm timing 을 periodInMinutes: 1 로 변경). pausedTabs cleanup 이 돌고 죽은 tab id 정리. 전후 DevTools 에서 chrome.storage 확인.
Hint
Host_permissions drop 후에도 install 경고 변함없으면 content_scripts.matches: ['<all_urls>'] 가 여전히 work — 좁히든가 (Lesson 3) activeTab + programmatic injection 으로 전환 (이 lesson). chrome.alarms.onAlarm 이 fire 안 하면, alarm 이 아마 생성 안 됨 chrome.runtime.onInstalled 가 아직 안 돌아서 — extension 제거하고 unpacked 디렉토리 다시 load 시도, fresh onInstalled trigger. MV3 의 chrome.alarms minimum period 가 30 초; 그 아래 값은 silent round-up.
Progress
Progress is local-only — sign in to sync across devices.