"MV3 service worker는 user 관점에서 두 상태밖에 없어: '지금 뭔가 하는 중' 과 'idle, evict 됐을 수도'. 그 on/off 형태 위에 설계하는 게 Track 2의 전체 mental model."
Idle clock은 즉시 시작
worker가 붙잡고 있던 마지막 event를 끝내는 순간 Chrome이 시계를 켜. 그러고 30 초쯤 아무 일도 없으면 — event도 안 오고, 기다리는 async도 없고, 굴러가는 Promise도 없으면 — worker를 내보내. 메모리가 풀리고, 전역이 사라지고, 최상위에 만들어 뒀던 Map 이든 Set 이든 같이 증발해.
Clock은 memory pressure 또는 Chrome 내부 휴리스틱이 worker가 안 쓸모 있다고 판단하면 더 빨리 만료. 30 초는 soft maximum으로 다뤄, 보장 아냐.
Worker를 깨우는 것
Worker가 listener 등록한 어떤 event 든. 큰 것들:
chrome.runtime.onMessage— popup, content script, 또는 다른 extension이 message 보냄chrome.alarms.onAlarm— 예약된 alarm fire (setTimeout/setInterval의 MV3 대체)chrome.tabs.*event — tab created / updated / removed / activated / replacedchrome.runtime.onInstalled/onStartup— extension install 또는 Chrome launchchrome.runtime.onConnect— popup 또는 content script에서 long-lived port 열림chrome.webNavigation.*— page navigation event (webNavigation permission 필요)chrome.contextMenus.onClicked— user가 등록된 context menu entry 활성화chrome.action.onClicked—default_popup없는 상태에서 toolbar icon 클릭
이 중 어느 게 fire 하면 Chrome이 worker를 cold로 깨워: background.js 위 부분 다시 실행, listener 재등록 (이게 중요 — 등록이 Chrome 한테 dispatch 하라는 신호), 그 다음 Chrome이 event 전달.
Eviction 때 죽는 것
Worker evict 때 잃는 거:
- Module-level 변수 (
let counter = 0,const cache = new Map()등) - Chrome의 event 제한보다 오래 걸리는 in-flight work. Extension API 호출은 idle timer를 다시 잡지만, 해결 안 된 Promise 자체는 durable storage가 아니고 긴 request에는 별도 시간 제한도 있어.
- WebSocket 연결, EventSource 연결, MediaStream handle, BroadcastChannel port
- Listener 안에서 이전 wake에 set 된 closure-captured state
살아남는 거:
chrome.storage.local— eviction 전에 write 됐으면 다음 wake에 read 가능chrome.storage.session— 2023 추가. worker eviction 견디지만 browser session 끝나면 죽음chrome.alarms— 등록된 alarm은 worker 재시작 너머로 영속- 등록된 listener 자체 (Chrome이 기억.
background.js위 부분 재실행이 재attach 메커니즘)
Cold-wake 패턴
Cold wake = worker가 매번 1 행에서부터 실행. background.js를 매 event 마다 도는 main() 함수처럼 다뤄:
- 해당 handler 위에서 필요한 state를
chrome.storage에서 read. - 일 하기.
- 새 state를
chrome.storage로 write back. - Handler return (또는 Promise resolve).
- Worker idle 됨. 결국 evict. 다음 event 대기.
가장 깔끔한 mental model: 모든 event handler가 전체 프로그램. Read / work / write / return.
피할 anti-pattern
MV2 에선 멀쩡해 보이지만 MV3 에선 능동적으로 깨지는 패턴:
- 주기적으로 뭘 돌리려고
setInterval(fn, 60000)을 걸면, timer가 울리기도 전에 worker가 쫓겨날 수 있어.chrome.alarms.create({periodInMinutes: 1})을 써. alarm은 퇴출을 넘어 살아남고, 때가 되면 worker를 다시 깨워 주거든. - Module-level cache (
const userCache = new Map()). 매 wake 마다 reset. Value에 명시적 expiry timestamp 박아서chrome.storage.local사용. - 조용한 WebSocket은 lifetime 보증이 아니야. Chrome 116부터 WebSocket traffic이 service worker 수명을 연장할 수 있지만 traffic이 끊기면 worker는 여전히 종료될 수 있어. 연결과 state를 복원 가능하게 설계해.
- Event 사이 "session state" 로 global 쓰기. Module-level 변수는 항상 ephemeral로 다뤄. 잠깐 살아남는 것처럼 보여도 의존하지 마.
chrome.storage 언급 없는 옛 답변은 의심해.