~11 min · chrome.action, manifest, badge, icon, popup
Level 0Extension 입덕
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"MV3에서 toolbar는 하나로 합쳐졌어. icon 하나, badge 하나, popup 하나, API 하나. 이 lesson은 chrome.action 연장통을 훑어 — 작고, 초점이 뚜렷하고, 뭔가 달라질 때마다 tab 별로 다시 칠할 수 있어."
action 하나로 통일
MV2 때는 개념이 둘로 갈려 있었어:
browser_action — 항상 보이는 toolbar icon. 대부분 extension의 기본 모양이었지.
page_action — 지금 페이지에 쓸모가 있을 때만 나타나는 icon (북마크 별표를 떠올리면 돼).
MV3는 이 둘을 chrome.action 하나로 합쳤어. toolbar에 자리를 원하는 extension은 manifest에 "action" object를 선언하고, 그러면 Chrome이 기본적으로 모든 tab에 icon을 보여 줘. 옛날 page-action 처럼 특정 페이지에서만 살아 있게 하고 싶으면, SW에서 chrome.action.disable(tabId) 랑 enable(tabId)를 그때그때 불러 주면 돼.
Manifest에 뭘 적나
최소한 이 셋이야:
action.default_icon — toolbar에 뜨는 icon. 경로 문자열 하나로 줘도 되고, size 별로 key를 나눈 object로 줘도 돼.
action.default_title — 마우스를 올렸을 때 뜨는 tooltip.
action.default_popup — 눌렀을 때 Chrome이 열어 줄 HTML 파일. SW에서 chrome.action.onClicked로 직접 받고 싶으면 이 줄을 빼면 돼.
icon은 16 / 32 / 48 / 128 픽셀짜리를 준비해. Chrome이 화면 밀도랑 설정을 보고 알맞은 걸 골라 써. size 별 key를 다 채워 주는 게 좋아. 그래야 Chrome이 한 장을 억지로 늘리는 대신 제대로 고르거든.
setBadgeText({ tabId?, text }) — icon 위에 작은 색 딱지를 얹어. 넉 자쯤이 한계라고 보면 돼.
setBadgeBackgroundColor({ tabId?, color }) — RGB 배열이나 hex 문자열.
setBadgeTextColor({ tabId?, color }) — Chrome 115부터.
setPopup({ tabId?, popup }) — 눌렀을 때 열 HTML을 바꿔.
disable(tabId?)와 enable(tabId?) — icon을 흐리게 만들어서 "이 페이지에선 할 게 없어" 를 알려.
getUserSettings() — user가 icon을 toolbar에 고정해 뒀는지, 아니면 퍼즐 조각 메뉴 안에 숨겨 뒀는지 읽어.
눈여겨볼 게 하나 있어. 조작 함수 전부가 tabId를 선택적으로 받아. tabId를 주면 그 tab만 바뀌고, 안 주면 따로 지정 안 한 모든 tab의 기본값이 바뀌어.
눌렀을 때 뭐가 일어나나
user가 icon을 누르면 둘 중 하나만 일어나. 둘이 동시에는 안 돼:
Popup 방식 (action.default_popup을 적어 두면 기본으로 이거야). 누르면 popup이 열리고, chrome.action.onClicked는 안 울려.
SW가 받는 방식 (default_popup이 없을 때). 누르면 SW의 chrome.action.onClicked가 울리고, 거기서 뭘 할지 정해 — side panel을 열든, script를 밀어 넣든, 메시지를 보내든.
돌아가는 중에도 둘 사이를 오갈 수 있어. chrome.action.setPopup({ tabId, popup: '' }) 처럼 popup을 빈 문자열로 두면 그 tab의 popup이 꺼지고 onClicked가 다시 살아나.
Badge는 신호 하나만
Badge는 작아. 대부분 넉 자쯤에서 잘리니까 한 가지만 또렷하게 전해:
숫자. 안 읽은 메시지, 오늘 저장한 clip, 밀린 작업.
상태. 모드를 알려 주는 두 글자 ("ON" / "OFF", "REC" / 빈칸).
경고. 봐 줘야 할 게 있을 때 빨간 느낌표 하나.
빈 문자열 (setBadgeText({ text: '' }))을 넣으면 badge가 지워져. ClipDeck은 오늘 저장한 clip 수 (Track 5 Lesson 5) 랑 tab 별 일시정지 상태에 badge를 쓸 거야.
chrome.action이 toolbar에서 쓸 수 있는 말 전부야 — icon, title, badge, popup, click handler. 조작 함수는 하나같이 tabId를 선택적으로 받아서 tab 별로 다르게 굴 수 있고. Badge 에는 문단을 쓰지 마. 신호 하나만 담아.
user가 icon을 고정했는지 확인해 봐. Chrome 91부터 chrome.action.getUserSettings()가 { isOnToolbar: boolean }를 돌려줘. false 면 icon이 퍼즐 조각 메뉴 뒤에 숨어 있다는 뜻이야. 처음 설치했을 때 이걸 확인해서 고정하는 법을 알려 주면 꽤 도움이 돼. 고정을 안 해 두면 user는 그런 게 있었다는 것부터 잊어버리거든.
Code
manifest.json — 여러 size icon을 갖춘 action block·json
// background.js — user 가 icon pin 했는지 감지 (Chrome 91+)
chrome.runtime.onInstalled.addListener(async (details) => {
if (details.reason !== "install") return;
try {
const settings = await chrome.action.getUserSettings();
if (!settings.isOnToolbar) {
console.log("[ClipDeck SW] icon is hidden behind the puzzle-piece menu");
// 첫 install onboarding 이 여기서 icon pin 법 설명하는 'Welcome' tab 열거나
// 일회성 notification 표시 가능.
}
} catch (err) {
// getUserSettings 는 Chrome 91+; 옛 Chrome 은 throw, 무시.
}
});
clipdeck/manifest.json을 첫 번째 code block (version 0.8.0)으로 올려. clipdeck/icons/ 밑에 16/32/48/128 픽셀 PNG를 실제로 만들어 둬 — 연습이니까 한 장을 네 번 복사해도 상관없어. badge 로직은 icon 품질 안 따지거든. 두 번째 code block (badge 갱신)을 background.js에 넣고 reload 해. Ctrl+Shift+K로 clip을 하나 저장하면 toolbar icon에 '1' 이 떠야 해. 몇 개 더 저장하면 badge도 따라 올라가고. 날짜가 바뀌면 (아니면 테스트로 todayStart를 한참 과거 날짜로 박아 두면) badge가 비워져. icon에 마우스를 올려서 tooltip이 개수를 말해 주는지도 봐. 여유가 되면 세 번째 code block도 넣고 ClipDeck을 지웠다 다시 설치해서 console 로그를 확인해 봐.
Hint
Storage와 alarm listener는 service-worker top level에 등록해. setBadgeBackgroundColor를 await하는 건 error handling에 좋지만 그게 color 적용 조건은 아니야. Midnight alarm이 없으면 어제 count가 다음 event까지 남을 수 있어.
Progress
Progress is local-only — sign in to sync across devices.