"Service worker 는 깨어나서, 한 가지 하고, 다시 잠드는 프로세스야. Extension 을 '대부분 잠들어 있다' 는 가정으로 설계하면, MV3 의 전체 디자인이 제자리에 딸각 들어와."
Background, 다시 상상하기
MV2 의 background page 는 Chrome 이 도는 동안 메모리에 계속 남아 있는 long-lived HTML 문서 + JavaScript runtime 이었어. 편하긴 했지 — global 에 state 박고, WebSocket 연결 유지, timer 돌리고. 비쌌고 — 모든 install 된 extension 이 user 가 쓰든 안 쓰든 메모리 비용. 보안 약점도 — long-lived 프로세스 한 번 compromise 되면 Chrome 재시작까지 compromise 유지.
MV3 가 persistent page 를 service worker 로 교체: Chrome 이 필요할 때 spin up 하고 idle 하면 tear down 하는 event-driven JavaScript context. PWA 를 굴리는 같은 개념을 extension 의 background context 로 재사용. 가볍고, 악용하기 어렵고, state 를 명시적으로 다루도록 강제 — 메모리에 기대지 마.
Service Worker 의 해부
Service worker 는 단일 JavaScript 파일 (HTML 없음, DOM 없음, window 없음). 자기 thread 에서 돌고 popup / content script 와 격리. 파일 위 부분이 매 wake-up 마다 실행, 거기 등록된 listener 가 event 받음.
window / document 못 써. localStorage 도 없음. fetch 됨. self 가 global. chrome.* API 는 popup 과 동일하게 동작, 한 가지만 다름: async 작업은 worker eviction 전에 완료해야 함 (또는 listener 에서 true 반환).
Service Worker Lifecycle
다섯 상태가 중요:
- Installing — Chrome 이 worker 등록; 파일 위 부분 한 번 실행.
- Activating — 등록 성공;
chrome.runtime.onInstalledfire. - Running — event 가 worker 깨움; 능동 실행 중.
- Idle — event 발동 없음, in-flight async 없음; Chrome 의 eviction clock 째깍.
- Evicted — Chrome 이 worker 종료. Module-level state 사라짐. Listener 는 여전히 등록 상태 (Chrome 의 worker 기록에 살고, worker memory 에 사는 게 아님).
다음 event — 등록된 listener 발동 — 이 worker 를 cold 로 깨움. Chrome 이 파일 위에서부터 다시 실행 (중요: listener 재등록이 chrome.runtime.onMessage.addListener(...) 를 파일 위에서 다시 실행함으로써 일어남) 한 다음 event dispatch.
왜 Idle eviction 이 중요해
세 가지 이유:
- 메모리 — 평균 user 는 install 된 extension 의 service worker 가 어느 순간이든 0 개 돌고 있음. Extension 이 능동적으로 일할 때만 메모리 spike.
- 보안 — Compromise 된 worker 가 데이터 exfiltrate 노리며 무한정 앉아 있을 수 없음; evict 되고, cold 로 재시작, Chrome 의 permission check 다시 돔.
- 성능 — Chrome 의 renderer process 가 always-on extension 코드로 비대해지지 않음.
비용은 정신적 전환: event 너머로 in-memory state 를 신뢰하지 마. 중요한 거는 chrome.storage 로. Ephemeral 한 거는 잃어도 OK.
ClipDeck 이 이걸로 뭐 할 거야
Track 2 를 통해 ClipDeck 이 첫 비-UI surface 얻음. Lesson 2 가 manifest 통해 background.js 등록. Lesson 3 이 event-driven lifecycle 깊이 들어감. Lesson 4 가 chrome.storage.local 을 state layer 로 도입. Lesson 5 가 popup ↔ worker message passing wire. Lesson 6 이 묶음: chrome.tabs.onUpdated 가 storage 의 방문 counter 증가, popup 이 running total 표시.
Track 2 끝나면 ClipDeck 이 더 이상 순수 UI 아냐 — background 에서 browser 관찰하고 state 누적. CRUD 의 R 워밍업.
embeds/chrome/background.js 처음 읽었을 때, 얼마나 작은지 놀랐어 — 총 4 KB. Per-tab context cache 를 Map 으로 (hot tab 한해서 Chrome 이 eviction 전에 보통 wake 하니까 acceptable), 최신 payload 를 side panel 로 forward, request-context handler 노출. 끝. Brain 은 cwkPippa 상단에 머물러. Worker 는 그냥 plumbing — 그게 모든 MV3 background script 의 맞는 형태야.