"Service worker가 깼다 잠들었다 하는 함수라면, chrome.storage는 그게 쓰는 기억이야. Lesson 4는 *중요한 건 다 storage에 살아야 한다* 는 사실이랑 화해하는 시간이고."
두 storage 영역: local과 sync
chrome.storage는 하위 namespace를 여럿 갖고 있어. 그중 제일 자주 쓸 둘부터:
chrome.storage.local— 머신 하나에 묶이고, extension을 지울 때까지 남아. MV3 기준 quota는 10 MB 쯤 되고, API는 Promise 기반이야. ClipDeck의 clip, 방문 카운트, 쌓이는 data는 전부 여기가 기본값이야.chrome.storage.sync— 로그인한 Chrome 끼리 동기화돼. 전체 100 KB, 항목 하나당 8 KB 쯤에서 막혀. 사용자 취향 (theme, hotkey 설정) 담는 데 쓰지, 쌓이는 data 담는 데는 아니야.
덜 쓰지만 알아 둘 것도 둘 있어. chrome.storage.session (2023에 추가)은 worker eviction은 견디는데 browser session이 끝나면 같이 죽어. chrome.storage.managed는 읽기 전용이고, 기업 정책이 대신 채워 줘.
전부 async 야
chrome.storage 작업은 하나도 빠짐없이 async 야. MV3 부터는 Promise를 돌려줘 (옛날 callback 방식도 나란히 살아 있고). 메서드 여섯 개면 거의 다 해결돼:
get(key | keys[] | undefined)— 하나, 여럿, 아니면 전부 읽기set(object)— 넘긴 object를 기존 데이터에 합쳐 (몇 번 불러도 같고, 상관없는 key는 안 건드려)remove(key | keys[])— key 지우기clear()— 이 영역 통째로 비우기getBytesInUse(key | keys[] | undefined)— quota 확인onChanged.addListener(fn)— 어느 context에서 쓰든 write가 나면 반응
set에 object를 넘기면 key를 합쳐. storage 전체를 갈아엎는 게 아니야. 그래서 { visitCount: 5 }를 써도 { clips: [...] }는 멀쩡해. storage 층이 기능끼리 안 부딪히고 조립되는 이유가 이거야.
JSON으로 되는 것만
chrome.storage는 값을 JSON으로 직렬화해. 그래서 실제로 이런 일이 생겨:
- 평범한 object, 배열, 문자열, 숫자, boolean, null — 다 괜찮아.
Dateobject → 들어갈 때 ISO 문자열이 되고, 꺼내면 문자열로 나와. 타입은 잃어버려.Map,Set→{}나 빈 배열이 돼 버려. 넣기 전에Object.fromEntries(map)이나[...set]로 직접 바꿔.- 함수, 메서드 달린 class instance, 순환 참조 — 조용히 망가지거나 대놓고 실패해.
JSON으로 옮길 수 있는 평범한 모양만 써. 특이한 타입은 넣을 때 평범한 값으로 바꾸고, 꺼낼 때 다시 조립하는 게 맞아.
변경 지켜보기
chrome.storage.onChanged는 popup 이든 side panel 이든 service worker 든 content script 든, 어디서 write가 나도 전부 fire 해. 그래서 popup이 worker가 쓴 값에 바로 반응할 수 있어. message를 주고받을 필요가 없지.
읽기 상태를 갱신하는 데는 이게 chrome.runtime.sendMessage 대신 쓸 수 있는 길이야. popup은 onChanged만 구독해 두고, SW가 storage에 쓰면, popup이 알아서 다시 그려. 왕복도 없고, "지금 worker 깨어 있나" 를 물어볼 일도 없어.
ClipDeck의 첫 storage 왕복
Lesson 6에서 ClipDeck 방문 카운터에 storage를 쓸 거야. 아래 두 번째 code block이 그 뼈대고. 읽고-처리하고-쓰고-다시-읽는 패턴의 가장 순수한 형태야. Module 수준에서는 아무 state도 안 들고 있고. Track 3부터 나오는 ClipDeck의 clip 목록도 같은 모양이야 — storage에서 배열 읽고, 뒤에 붙이고, 다시 쓰기. Track 7의 edit과 delete도 똑같아 — 읽고, 고치고, 쓰기.
이 패턴 하나가 몸에 붙으면 ClipDeck의 나머지는 거의 다 그 변주야.
get → modify → set을 surface 둘이 동시에 돌리면 한쪽 변경이 사라질 수 있어. Service worker를 sole writer로 두고 mutation queue 하나로 직렬화하거나 record를 독립 key로 저장해. 한 번의 set은 그 안의 key를 함께 적용하지만, 앞뒤 read-modify-write 전체가 atomic인 건 아니야.