content script의 문지기. 뭘 담기 전에 content script가 storage를 확인해. 멈춰 있으면 아무것도 안 하고 그대로 답하고, SW도 아무것도 안 남겨.
SW는 지금 tab의 멈춤 상태가 바뀔 때마다 badge도 같이 고쳐. 멈춰 있으면 setBadgeText({ tabId, text: 'II' }), 아니면 빈 값으로.
Race-free update
배열을 읽고 나서 쓰는 사이에 event 두 개가 붙어서 오면 (user가 popup 버튼을 누르는 동시에 단축키도 눌렀다든지) 서로 밟을 수 있어. 방어하는 방법은, 고칠 때마다 그 자리에서 다시 읽는 작은 helper를 하나 두는 거야:
async function togglePauseForTab(tabId) {
const { pausedTabs = [] } = await chrome.storage.session.get('pausedTabs');
const next = pausedTabs.includes(tabId)
? pausedTabs.filter((t) => t !== tabId)
: [...pausedTabs, tabId];
await chrome.storage.session.set({ pausedTabs: next });
return next.includes(tabId);
}
엄밀히 원자적이진 않아. get과 set 사이에 다른 event가 끼어들 수 있거든. 그래도 사람 손 속도로 누르는 토글이라면 실전에선 충분해. 나중에 ClipDeck이 창 여러 개를 맞춰야 할 만큼 자라면, 그때 sequence id를 붙인 제대로 된 잠금 방식으로 갈아타면 돼.
시각 피드백
badge가 정직한 신호 노릇을 해. 상태는 셋이야:
켜져 있고 오늘 저장한 게 있으면 → 브랜드 색에 숫자 ("3").
켜져 있는데 오늘 저장한 게 없으면 → 빈 badge.
멈춰 있으면 → 회색 바탕에 "II" 나 "PAUSE".
storage가 바뀔 때마다 개수 모드랑 멈춤 모드 사이를 오가. popup도 버튼 글자를 뒤집어 — "이 사이트에서 멈추기" ↔ "이 사이트에서 다시 켜기". side panel은 user가 멈춰 둔 tab을 보고 있으면 띠를 하나 띄워 줘도 좋아 ("지금은 안 담고 있어. 다시 켤까?").
Track 5 마무리
이 lesson까지 오면 ClipDeck이 toolbar 쪽 표면을 다 갖춰:
오늘 개수랑 멈춤 표시를 겸하는 badge를 단 icon.
요약이랑 주요 동작 셋, 그리고 눈에 띄는 설정 링크가 있는 popup.
고른 텍스트가 그대로 이름에 들어가는 우클릭 메뉴.
user가 원하는 대로 바꿀 수 있는 단축키 넷.
주소창 검색 키워드 clip.
어디에서 봐도 똑같이 반영되는 tab 별 멈춤과 재개.
다음으로 자연스러운 건 Track 6의 permission 모델이야. ClipDeck이 왜 하필 그 권한을 달라고 하는지, 꼭 필요한 순간에만 묻는 법, 그리고 거절하는 user를 어떻게 대접할지.
멈춤 스위치는 user가 신뢰를 조절하는 다이얼이야. tab 단위로 걸리고, storage에 남고, badge와 popup에 똑같이 비쳐. 표면은 셋인데 상태는 하나야. 그래서 놀랄 일이 없고.
수명이 ownership을 정해. Clip과 표시 설정은 browser restart를 넘어야 하니 storage.local, tab 단위 pause는 그 tab과 browser session 안에서만 의미가 있으니 storage.session에 둬. Content script를 경쟁 writer로 만들지 말고 service worker 하나가 pause gate와 cleanup을 맡아.
Tab ID는 runtime 식별자라 browser restart 뒤 재사용될 수 있어. pausedTabs는 storage.session에 두고 tabs.onRemoved에서 치워. Gate는 service worker가 맡아. Clip과 display preference는 storage.local, tab-scoped pause state는 browser session에만 남겨.
Code
background.js — toggle + badge refresh + tab close cleanup·javascript
첫 번째 code block을 clipdeck/background.js에 넣어 — togglePauseForTab helper, tab 별 badge 갱신, command 연결, tab 닫을 때 정리까지 들어 있어. 두 번째 code block은 clipdeck/popup.js에 넣고 (Pause 버튼 handler 랑 실시간 라벨), 그 아래 주석으로 달린 togglePause 메시지 라우터는 background.js에 넣어. 기존 save-clip handler는 세 번째 code block의 문지기 달린 버전으로 갈아. Reload 하고 아무 페이지에서 popup을 열면 Pause on this tab 이 보일 거야. 눌러 봐 — badge가 회색 II 로 바뀌고 글자가 Resume on this tab 으로 뒤집혀. 그 상태에서 Ctrl+Shift+K를 눌러도 아무것도 안 저장돼야 해. 다시 Resume on this tab 을 누르면 badge가 돌아오고 저장도 다시 되고. tab을 옮겨 다니면서 badge가 tab 마다 따로 논다는 것도 확인해. 마지막으로 멈춰 둔 tab을 닫았다가 같은 주소를 다시 열어 봐 — 안 멈춘 상태로 시작하는 게 맞아.
Hint
멈췄는데 badge가 II 로 안 바뀌면 chrome.action.setBadgeText 에 await를 안 건 거야. 그거 Promise 야. popup에서 togglePause 가 Receiving end does not exist 를 뱉으면 SW 쪽에 그 메시지를 받을 listener를 안 걸어 둔 거고 (두 번째 code block 맨 아래 주석으로 달아 둔 라우터를 넣어). 멈춘 상태인데도 save-clip이 계속 저장하면 SW 쪽 문지기가 handler에 안 들어간 거야. save-clip 을 받는 기존 chrome.commands.onCommand listener를 찾아서 맨 앞에 멈춤 확인을 끼워 넣어.
Progress
Progress is local-only — sign in to sync across devices.